python-3.x - hasattr 说谎?(AttributeError:“方法”对象没有属性“__annotations__”)
问题描述
以下代码
class Foo:
def bar(self) -> None:
pass
foo = Foo()
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__ = 'hi'
崩溃
AttributeError: 'method' object has no attribute '__annotations__'
这怎么可能发生?
解决方案
此处出现属性错误是因为您无法在方法对象上设置任何属性:
>>> foo.bar.baz = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method' object has no attribute 'baz'
这里的异常可能令人困惑,因为method
对象包装了一个函数对象,并且代理属性读取访问该底层函数对象。因此,当函数上的属性存在时,hasattr()
方法上将返回True
:
>>> hasattr(foo.bar, 'baz')
False
>>> foo.bar.__func__.baz = 42
>>> hasattr(foo.bar, 'baz')
True
>>> foo.bar.baz
42
但是,您仍然无法通过该方法设置这些属性,无论:
>>> hasattr(foo.bar, 'baz')
True
>>> foo.bar.baz = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method' object has no attribute 'baz'
因此,仅仅因为可以读取属性并不意味着您可以设置它。hasattr()
说的是实话,你只是把它解释为不同的意思。
现在,如果您尝试__annotations__
直接在底层函数对象上设置属性,您会收到另一条错误消息:
>>> foo.bar.__func__.__annotations__ = 'hi'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __annotations__ must be set to a dict object
你会想在这里使用一个字典对象:
>>> foo.bar.__func__.__annotations__ = {'return': 'hi'}
>>> foo.bar.__annotations__
{'return': 'hi'}
然而,由于__annotations__
是一个可变字典,直接操作该对象的键和值就更容易了,这通过方法包装器完全可行:
>>> foo.bar.__annotations__['return'] = 'int'
>>> foo.bar.__annotations__
{'return': 'int'}
现在,如果您希望为每个实例设置注释,则无法在方法对象上设置属性,因为方法对象是短暂的,它们是为调用而创建的,然后通常在之后被丢弃。
__annotations__
您必须通过元类使用自定义方法描述符对象并每次为这些对象重新创建属性,或者您可以改为使用新的函数对象预先绑定方法,该函数对象将被赋予其自己的属性。然后您必须支付更大的内存价格:
import functools
foo.bar = lambda *args, **kwargs: Foo.bar(foo, *args, **kwargs)
functools.update_wrapper(foo.bar, Foo.bar) # copy everything over to the new wrapper
foo.bar.__annotations__['return'] = 'hi'
无论哪种方式,您都会以这种方式完全扼杀 Python 3.7 中的重要速度优化。
并且在最重要的用例上运行的工具__annatotions__
,类型提示,实际上并不执行代码,它们静态读取代码并且会完全错过这些运行时更改。
推荐阅读
- c# - 基于属性数量的自定义 JSON 格式
- javascript - 如何使用 $geowithin 查找文档,然后使用 $geonear 聚合管道创建距离场?
- javascript - 问题检查元素是否在 javascript 中处于“活动状态”
- html - 不同列中第一个 div 的高度相等(引导程序 4)
- arrays - 在具有 React 状态的 div 网格中跟踪选定的 div
- python - 使用 django 形式执行一些计算。哪个框架?
- php - 模型观察者:restored() 和 restore()
- html - 如何让图像和其他元素保持原位
- swift - 这个 RxAlamofire 源代码中的实际 API 调用在哪里?
- azure - 整个字段的 Azure 搜索筛选器