以下代码

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/

10-12 21:22