python - 使用 @properties 提高 AttributeError 会产生意想不到的副作用
问题描述
给定一个人为的 Python 类:
class Item(object):
def __getattr__(self, name):
print("In __getattr__ looking for '{}'".format(name))
if name.startswith("exists"):
return "Here's a dummy string"
message = "'{}' object has no attribute '{}'".format(type(self).__name__, name)
raise AttributeError(message)
def thing_a(self):
return self.thing_b()
def thing_b(self):
return self.nonexistent_thing()
调用Item().thing_a()
会产生预期的输出:
In __getattr__ looking for 'nonexistent_thing'
Traceback (most recent call last):
File "debug.py", line 13, in <module>
Item().thing_a()
File "debug.py", line 8, in thing_a
return self.thing_b()
File "debug.py", line 11, in thing_b
return self.nonexistent_thing()
File "debug.py", line 5, in __getattr__
raise AttributeError(message)
AttributeError: 'Item' object has no attribute 'nonexistent_thing'
但是,我真的希望thing_a
,thing_b
和nonexistent_thing
成为属性。因此,如果我将代码更改为:
class Item(object):
def __getattr__(self, name):
print("In __getattr__ looking for '{}'".format(name))
if name.startswith("exists"):
return "Here's a dummy string"
message = "'{}' object has no attribute '{}'".format(type(self).__name__, name)
raise AttributeError(message)
@property
def thing_a(self):
return self.thing_b
@property
def thing_b(self):
return self.nonexistent_thing
并打电话Item().thing_a
,我得到了一个意想不到的结果:
In __getattr__ looking for 'nonexistent_thing'
In __getattr__ looking for 'thing_b'
In __getattr__ looking for 'thing_a'
Traceback (most recent call last):
File "debug.py", line 15, in <module>
Item().thing_a
File "debug.py", line 5, in __getattr__
raise AttributeError(message)
AttributeError: 'Item' object has no attribute 'thing_a'
呃-哦...print()
输出似乎与执行顺序相反,并且消息 forAttributeError
是 for thing_a
,确实存在。
在我尝试解决这个问题的过程中,我发现更改AttributeError
为普通的旧Exception
版本可以让一切正常:
In __getattr__ looking for 'nonexistent_thing'
Traceback (most recent call last):
File "debug.py", line 15, in <module>
Item().thing_a
File "debug.py", line 9, in thing_a
return self.thing_b
File "debug.py", line 13, in thing_b
return self.nonexistent_thing
File "debug.py", line 5, in __getattr__
raise Exception(message)
Exception: 'Item' object has no attribute 'nonexistent_thing'
所以,它AttributeError
本身似乎不能很好地与 一起使用@property
,可能是因为它在某个地方被property
对象抓住了。但AttributeError
似乎是最合适的异常情况__getattr__
,并且在没有子类化的情况下提高情况并不理想Exception
。
有没有人能够对这个问题有所了解,并且知道一种@property
和谐合作的__getattr__
方法?AttributeError
我已经在 Python 2.7 和 3.6 中尝试过。
编辑:添加(人为)逻辑__getattr__
以响应 ifname
开头"exists"
。我确实希望它失败nonexistent_thing
,但我希望它AttributeError
在正确的时间以正确的方式加注name
。
解决方案
谜题中缺少的部分@property
是描述符(实现__get__
//参与属性查找的对象)。__set__
__del__
描述符的实现是通过__getattribute__
魔术方法完成的。
文档中实现的重要一点(强调我的):
无条件调用以实现类实例的属性访问。如果该类还定义了
__getattr__()
,则不会调用后者,除非__getattribute__()
显式调用它或引发 AttributeError。
为了进一步说明您的示例:
class Item(object):
def __getattribute__(self, name):
print(f'__getattribute__ {name}')
try:
return super().__getattribute__(name)
except Exception as e:
print(f'{e} during __getattribute__ {name}')
raise
def __getattr__(self, name):
print("In __getattr__ looking for '{}'".format(name))
# useful logic here which might return a value...
message = "'{}' object has no attribute '{}'".format(type(self).__name__, name)
raise AttributeError(message)
@property
def thing_a(self):
return self.thing_b
@property
def thing_b(self):
return self.nonexistent_thing
Item().thing_a
调用顺序如下:
- __getattribute__(thing_a)
- property.__get__(...)
- thing_a
- __getattriute__(thing_b)
- property.__get__(...)
- thing_B
- __getattribute__(nonexistent_thing)
- (raised AE) -> __getattr__(nonexistent_thing)
- (raised AE) -> __getattr_(thing_b)
- (raised AE) -> getattr(thing_a)
在描述符解析期间对 s的调用__getattr__
是对 s 的响应。AttributeError
__getattribute__
以下是上述 python 片段的输出:
__getattribute__ thing_a
__getattribute__ thing_b
__getattribute__ nonexistent_thing
'Item' object has no attribute 'nonexistent_thing' during __getattribute__ nonexistent_thing
In __getattr__ looking for 'nonexistent_thing'
'Item' object has no attribute 'nonexistent_thing' during __getattribute__ thing_b
In __getattr__ looking for 'thing_b'
'Item' object has no attribute 'thing_b' during __getattribute__ thing_a
In __getattr__ looking for 'thing_a'
Traceback (most recent call last):
File "t.py", line 25, in <module>
Item().thing_a
File "t.py", line 14, in __getattr__
raise AttributeError(message)
AttributeError: 'Item' object has no attribute 'thing_a'
推荐阅读
- javascript - Javascript将对象附加到对象
- django - uuid.uuid4() 不在 DJango 表单中创建随机 UUID
- c# - WPF DevExpress HamburgerMenu ItemTemplateSelector 容器为空
- spring - 使用 TransactionalOperator 进行单元测试超时(Kotlin 协程)
- angular - 从 Angular 8 更新到 Angular 11 后,Angular 的服务器端渲染停止工作
- airflow - 如何删除旧的 Xcom?
- node.js - 打字稿错误:类扩展值未定义不是构造函数
- javascript - 自定义元素选择器 - Javascript
- vba - 使用是/否消息框选项将单元格转换为文本
- python - 如何在自动 arima 中设置 P、D、Q