如何在嵌套调用中优先调用setattr而非getattr?实现可编辑嵌套结构
实现支持嵌套赋值自动创建、访问不存在属性抛错的嵌套结构
核心需求实现思路
要实现「赋值时自动创建中间属性,访问不存在属性时抛出AttributeError」,关键在于区分赋值场景和单纯访问场景:
- 赋值场景(如
e.a.b.c = 1):需要自动创建路径中不存在的中间实例 - 单纯访问场景(如
print(e.a.b.d)):必须抛出异常,不能自动创建
现有代码的问题在于:
__getattr__与get方法绑定,访问不存在属性时会自动创建实例,导致行为类似defaultdict,不符合访问抛错的需求set方法中引用了未定义的self.delimiter属性,会触发报错get方法内部嵌套定义同名函数,存在逻辑混乱,且@wraps(self.get)的用法错误
解决方案代码
通过引入代理类(_Proxy)来处理赋值路径中的属性访问,只有当实际执行赋值操作时才会创建对应实例,单纯访问时则抛出异常:
from typing import Any, Optional class _Proxy: def __init__(self, parent, attr_name): self._parent = parent self._attr_name = attr_name def _resolve(self): # 递归解析出实际的Example实例,不存在则创建 if isinstance(self._parent, _Proxy): parent_instance = self._parent._resolve() else: parent_instance = self._parent if not hasattr(parent_instance, self._attr_name): setattr(parent_instance, self._attr_name, Example()) return getattr(parent_instance, self._attr_name) def __getattr__(self, name): # 访问代理属性时,返回新的代理对象 return _Proxy(self, name) def __setattr__(self, name, value): if name.startswith('_'): super().__setattr__(name, value) return # 执行赋值时,先解析出实际实例,再完成赋值 target = self._resolve() setattr(target, name, value) def __repr__(self): # 单纯访问代理对象时,抛出属性不存在异常 raise AttributeError(f"'Example' object has no attribute '{self._attr_name}'") __str__ = __repr__ class Example: def __getattr__(self, name: str) -> Any: # 属性不存在时返回代理对象,而非直接创建实例 return _Proxy(self, name) def __setattr__(self, name: str, value: Any) -> None: # 直接设置当前实例的属性 super().__setattr__(name, value) def __getitem__(self, name: str) -> Any: # 支持索引式访问,与属性访问逻辑一致 return getattr(self, name) def __setitem__(self, name: str, value: Any) -> None: setattr(self, name, value) def get(self, name: str, default: Optional[Any] = None) -> Any: # 支持点路径的get方法,不存在则返回默认值 parts = name.split('.') current = self for part in parts: try: current = getattr(current, part) except AttributeError: return default return current
测试验证
e = Example() # 嵌套赋值:自动创建中间实例 e.a.b.c = 1 print(e.a.b.c) # 输出: 1 print(e.a.b) # 输出: <__main__.Example object at 0x...> # 访问不存在属性:抛出AttributeError try: print(e.a.b.d) except AttributeError as err: print(err) # 输出: 'Example' object has no attribute 'd' try: print(e.x) except AttributeError as err: print(err) # 输出: 'Example' object has no attribute 'x' # get方法支持默认值 print(e.get("a.b.c")) # 输出: 1 print(e.get("a.b.d", 0)) # 输出: 0
内容的提问来源于stack exchange,提问作者Zhiyuan Chen
相关产品推荐
相关产品推荐

