You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中如何实现无__getattr__的属性链式访问(含虚拟属性)?

Python链式虚拟属性访问实现方案

先看你定义的类:

class Blah:
    def __init__(self):
       self.foo = ...
       self.bar = ...

已知foo和bar没有实现__getattr__,也没有abc/xyz属性,甚至可能是虚拟属性(即实例中实际不存在),下面分两种情况说明如何实现链式属性访问:


第一种:b.foo.abc / b.bar.xyz

要实现这种先访问foo/bar再访问子属性的链式操作,核心是让Blah实例在访问foo/bar时返回一个代理对象,由这个代理对象处理后续的任意属性访问(比如abc/xyz)。

如果foo/bar是虚拟属性(实例中不存在),可以用Blah的__getattr__方法拦截对它们的访问,返回代理对象:

class AttrProxy:
    def __getattr__(self, attr_name):
        # 这里可根据需求返回具体值,比如None、自定义结果,或继续返回代理
        # 示例:返回带标识的字符串
        return f"processed_{attr_name}"

class Blah:
    def __getattr__(self, attr_name):
        # 拦截对foo/bar的虚拟属性访问
        if attr_name in ("foo", "bar"):
            return AttrProxy()
        # 其他属性正常抛出错误
        raise AttributeError(f"'Blah' object has no attribute '{attr_name}'")

# 测试
b = Blah()
x = b.foo.abc  # 得到 "processed_abc"
y = b.bar.xyz  # 得到 "processed_xyz"

如果foo/bar实际存在但没有子属性,也可以在Blah的__init__里直接给self.foo/self.bar赋值为代理对象,效果一致。


第二种:b.abc.foo / b.xyz.bar

这种先访问abc/xyz再访问foo/bar的场景,需要Blah实例拦截对abc/xyz的访问,返回一个能处理后续foo/bar访问的代理对象:

class ReverseAttrProxy:
    def __init__(self, parent_attr):
        self.parent_attr = parent_attr
    
    def __getattr__(self, attr_name):
        # 结合父属性和当前属性生成结果,逻辑可自定义
        return f"{self.parent_attr}_{attr_name}"

class Blah:
    def __getattr__(self, attr_name):
        # 拦截对abc/xyz的访问
        if attr_name in ("abc", "xyz"):
            return ReverseAttrProxy(attr_name)
        raise AttributeError(f"'Blah' object has no attribute '{attr_name}'")

# 测试
b = Blah()
x = b.abc.foo  # 得到 "abc_foo"
y = b.xyz.bar  # 得到 "xyz_bar"

同时支持两种链式访问

如果需要同时兼容两种访问顺序,可以写一个更灵活的代理,记录完整的属性访问路径,后续根据路径做业务处理:

class FlexibleProxy:
    def __init__(self, path=None):
        self.path = path or []
    
    def __getattr__(self, attr_name):
        # 拼接新的访问路径,返回新的代理实例
        return FlexibleProxy(self.path + [attr_name])
    
    def __repr__(self):
        # 用字符串展示完整访问路径
        return f"AccessPath({'.'.join(self.path)})"

class Blah:
    def __getattr__(self, attr_name):
        # 所有未定义的属性都返回代理
        return FlexibleProxy([attr_name])

# 测试两种场景
b = Blah()
print(b.foo.abc)   # AccessPath(foo.abc)
print(b.bar.xyz)   # AccessPath(bar.xyz)
print(b.abc.foo)   # AccessPath(abc.foo)
print(b.xyz.bar)   # AccessPath(xyz.bar)

你可以在FlexibleProxy里添加逻辑,比如根据路径返回对应的数据、调用接口等,完全自定义行为。


内容的提问来源于stack exchange,提问作者sten

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 17:09:45