以下代码
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__
属性,或者您可以将方法与将被赋予其自己属性的新函数对象预先绑定(bind)。然后,您必须付出更大的内存价格: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'
无论哪种方式,您都以这种方式completely kill important speed optimisations made in Python 3.7。
在
__annatotions__
的最重要用例上运行的工具,类型提示,实际上不执行代码,它们静态地读取代码并且会完全错过这些运行时更改。关于python-3.x - hasattr说谎吗? (AttributeError : 'method' object has no attribute '__annotations__' ),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53450624/