Zha*_*iel 6 python python-datamodel python-descriptors
我偶然发现了这段代码,我觉得很奇怪,因为它似乎违反了 python 内置函数直接从对象的类调用 dunder 方法的事实。举__call__个例子,如果我们定义类A如下:
class A:\n @property\n def __call__(self):\n def inner():\n return 'Called.'\n return inner\n\na = A()\na() # return 'Called.'\n\ntype(a).__call__(a) # return 'property' object is not callable. \nRun Code Online (Sandbox Code Playgroud)\n然而,这种行为似乎与Python官方文档中所说的相矛盾:
\n\n\n\n
object.__call__(self[, args...])当实例\xe2\x80\x9c称为\xe2\x80\x9d\nas函数时调用;如果定义了此方法,则x(arg1, arg2, ...)大致\n转换为type(x).__call__(x, arg1, ...).
谁能解释一下这是怎么回事?
\n是的,但它尊重从给定函数检索方法的方式 - 我们可以看到该__get__ 方法被调用:
在下面的代码中,我只是property用一个更简单的描述符替换,该描述符将检索其“func” - 并将其用作方法__call__。
In [34]: class X:
...: def __init__(self, func):
...: self.func = func
...: def __get__(self, instance, owner):
...: print("descriptor getter called")
...: return self.func
...:
In [35]: class Y:
...: __call__ = X(lambda: "Z")
...:
In [36]: y = Y()
In [37]: y()
descriptor getter called
Out[37]: 'Z'
Run Code Online (Sandbox Code Playgroud)
因此,“dunder”功能只是像往常一样通过所有方法检索方法__get__。被跳过的是所经历的步骤__getattribute__- Python 将直接进入类__call__中的槽A,而不是通过调用__getattribute__从类开始(对于描述符)的正常查找序列,然后查看实例,而不是回到类(对于常规属性):它假设如果实例的类 dunder 插槽中有某些东西,那么它是一个方法,并__get__相应地使用它的方法。
__get__从实例检索函数时使用函数- 因为这是注入实例作为self调用参数的机制。
而正是替换来发挥其“魔力”的__get__东西。property
为了证明它__getatribute__没有被调用,在 iPython 中,我必须将“y”封装在一个虚拟函数中,否则 iPython__getattribute__在尝试自动完成内容时会触发:
In [42]: class Y:
...: __call__ = X(lambda: "Z")
...: def __getattribute__(self, name):
...: print("getattribute called")
...: return super().__getattribute__(name)
...:
In [43]: def w():
...: y = Y()
...: return y()
...:
In [44]: w()
descriptor getter called
Out[44]: 'Z'
# in contrast with:
In [46]: Y().__call__()
getattribute called
descriptor getter called
Out[46]: 'Z'
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
89 次 |
| 最近记录: |