Det*_*ant 5 python methods function method-call
来自Python文档中\xe2\x80\x98数据模型\xe2\x80\x99章节的\xe2\x80\x98新样式类的特殊方法查找\ xe2\x80\x99部分(粗体强调是我的):
\n\n\n对于新式类,只有在 object\xe2\x80\x99s 类型上定义特殊方法的隐式调用才能保证正确工作,而不是在 object\xe2\x80\x99s 实例字典中定义。这种行为就是以下代码引发异常的原因(与旧式类的等效示例不同):
\nRun Code Online (Sandbox Code Playgroud)\n>>> class C(object):\n... pass\n...\n>>> c = C()\n>>> c.__len__ = lambda: 5\n>>> len(c)\nTraceback (most recent call last):\n File "<stdin>", line 1, in <module>\nTypeError: object of type \'C\' has no len()\n此行为背后的基本原理在于由所有对象(包括类型对象)实现的许多特殊方法,例如
\n__hash__()和\n 。__repr__()如果这些方法的隐式查找\n使用传统的查找过程,则在类型对象本身上调用时它们将失败:Run Code Online (Sandbox Code Playgroud)\n>>> 1 .__hash__() == hash(1)\nTrue\n>>> int.__hash__() == hash(int)\nTraceback (most recent call last):\n File "<stdin>", line 1, in <module>\nTypeError: descriptor \xe2\x80\x99__hash__\xe2\x80\x99 of \xe2\x80\x99int\xe2\x80\x99 object needs an argument\n错误地尝试以这种方式调用类的未绑定方法有时被称为 \xe2\x80\x98metaclass\nconfusion\xe2\x80\x99,并且可以通过在查找特殊方法时绕过实例来避免:
\nRun Code Online (Sandbox Code Playgroud)\n>>> type(1).__hash__(1) == hash(1)\nTrue\n>>> type(int).__hash__(int) == hash(int)\nTrue\n
我听不懂粗体字\xe2\x80\xa6
\n要了解这里发生的情况,您需要对传统属性查找过程有(基本)了解。举一个典型的面向对象编程入门示例 -fido是Dog:
class Dog(object):
pass
fido = Dog()
Run Code Online (Sandbox Code Playgroud)
如果我们说fido.walk(),Python 做的第一件事就是查找walk在 中调用的函数fido(作为 中的条目fido.__dict__)并在不带参数的情况下调用它 - 所以,它的定义如下:
def walk():
print "Yay! Walking! My favourite thing!"
fido.walk = walk
Run Code Online (Sandbox Code Playgroud)
并且fido.walk()会起作用。walk如果我们没有这样做,它会在type(fido)(即) 中查找属性Dog并使用实例作为第一个参数调用它 (即self) - 这是由我们在 Python 中定义方法的常用方式触发的:
class Dog:
def walk(self):
print "Yay! Walking! My favourite thing!"
Run Code Online (Sandbox Code Playgroud)
现在,当您调用 时repr(fido),它最终会调用特殊方法__repr__。它可能(不太好,但说明性地)定义如下:
class Dog:
def __repr__(self):
return 'Dog()'
Run Code Online (Sandbox Code Playgroud)
但是,粗体文字表明这样做也是有意义的:
repr(Dog)
Run Code Online (Sandbox Code Playgroud)
在我刚刚描述的查找过程中,它查找的第一件事是一个名为“__repr__分配给Dog...”的方法,嘿,看,有一个,因为我们只是很糟糕但说明性地定义了它。所以,Python 调用:
Dog.__repr__()
Run Code Online (Sandbox Code Playgroud)
它在我们脸上爆炸:
>>> Dog.__repr__()
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
Dog.__repr__()
TypeError: __repr__() takes exactly 1 argument (0 given)
Run Code Online (Sandbox Code Playgroud)
因为__repr__()期望将一个Dog实例作为其参数传递给它self。我们可以这样做来让它工作:
class Dog:
def __repr__(self=None):
if self is None:
# return repr of Dog
# return repr of self
Run Code Online (Sandbox Code Playgroud)
但是,每次编写自定义__repr__函数时,我们都需要执行此操作。它需要知道如何找到__repr__类的 ,这是一个问题,但问题不大——它可以委托给Dog自己的类 ( type(Dog)) 并调用它的__repr__withDog作为它的self参数:
if self is None:
return type(Dog).__repr__(Dog)
Run Code Online (Sandbox Code Playgroud)
但首先,如果类名将来发生变化,这就会中断,因为我们需要在同一行中提及它两次。但更大的问题是,这基本上是样板文件:99% 的实现只会向上委托链,或者忘记这样做,从而出现错误。因此,Python 采用这些段落中描述的方法 -repr(foo)跳过查找__repr__附加到foo,并直接进入:
type(foo).__repr__(foo)
Run Code Online (Sandbox Code Playgroud)