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
相关产品推荐
相关产品推荐

