任何人都可以在python参考中解释以下段落吗?

时间:2012-09-01 12:19:31

标签: python

来自refence中的Python数据模型章节的Special method lookup for new-style classes section

  

对于新式类,只有在定义时,才能保证对特殊方法的隐式调用才能正常工作   对象的类型,而不是对象的实例字典。这种行为是以下代码引发的原因   一个例外(与旧式类的等效示例不同):

>>> class C(object):
...     pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()
     

此行为背后的基本原理在于许多特殊方法,例如__hash__()和。{   __repr__()由所有对象实现,包括类型对象。如果隐式查找这些方法   使用传统的查找过程,它们在类型对象本身上调用时会失败:

>>> 1 .__hash__() == hash(1)
True
>>> int.__hash__() == hash(int)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor ’__hash__’ of ’int’ object needs an argument
     

以这种方式错误地尝试调用类的未绑定方法有时被称为“元类”   在查找特殊方法时绕过实例可以避免混淆:

>>> type(1).__hash__(1) == hash(1)
True
>>> type(int).__hash__(int) == hash(int)
True

我无法用大胆的方式抓住这些词......

3 个答案:

答案 0 :(得分:2)

要了解这里发生了什么,您需要对传统的属性查找过程有一个(基本的)理解。以典型的面向对象编程示例为例 - fidoDog

class Dog(object):
    pass

fido = Dog()

如果我们说fido.walk(),Python所做的第一件事就是在walk中寻找一个名为fido的函数(作为fido.__dict__中的一个条目)并调用它没有参数 - 所以,一个被定义为这样的东西:

def walk():
   print "Yay! Walking! My favourite thing!"

fido.walk = walk

fido.walk()将有效。如果我们没有这样做,它会在walk中查找属性type(fido)Dog),并以实例作为第一个参数调用它(即{{1}这是由我们在Python中定义方法的通常方式触发的:

self

现在,当您致电class Dog: def walk(self): print "Yay! Walking! My favourite thing!" 时,它最终会调用特殊方法repr(fido)。它可能(很差,但说明性地)定义如下:

__repr__

但是,大胆的文字说这样做也是有道理的:

class Dog:
    def __repr__(self):
          return 'Dog()'

在我刚刚描述的查找过程中,它首先查找的是一个名为 repr(Dog) 的方法,分配给__repr__ ...嘿,看,有一个,因为我们只是很差但是说明性地定义它。所以,Python调用:

Dog

它在我们脸上爆炸:

Dog.__repr__()

因为>>> Dog.__repr__() Traceback (most recent call last): File "<pyshell#38>", line 1, in <module> Dog.__repr__() TypeError: __repr__() takes exactly 1 argument (0 given) 期望__repr__()实例作为其Dog参数传递给它。我们可以这样做,以使其工作:

self

但是,我们每次编写自定义class Dog: def __repr__(self=None): if self is None: # return repr of Dog # return repr of self 函数时都需要执行此操作。它需要知道如何找到类的__repr__是一个问题,但不是一个问题 - 它可以委托给__repr__自己的类(Dog)和将type(Dog)称为__repr__作为其Dog - 参数:

self

但首先,如果类名在将来发生变化,这会中断,因为我们需要在同一行中提及它两次。但更大的问题是,这基本上将成为样板:99%的实现只会委托链,或者忘记,因此是错误的。因此,Python采用这些段落中描述的方法 - if self is None: return type(Dog).__repr__(Dog) 跳过查找附加到repr(foo)的{​​{1}},然后直接进入:

__repr__

答案 1 :(得分:0)

您必须记住的是,类是元类的实例。某些操作不仅需要在实例上执行,还需要在类型上执行。如果实例上的方法被运行,那么它将失败,因为实例上的方法(在这种情况下实际上是一个类)将需要类的实例而不是元类。

class MC(type):
  def foo(self):
    print 'foo'

class C(object):
  __metaclass__ = MC
  def bar(self):
    print 'bar'

C.foo()
C().bar()
C.bar()

答案 2 :(得分:0)

普通属性检索obj.attrattrinstance属性和class属性中查找obj。它在 object.__getattribute__type.__getattribute__ 中定义。

隐式特殊方法调用special(obj, *args, **kwargs)(例如hash(1))在__special__的类属性(例如__hash__)中查找obj(例如1) }}),绕过obj的实例属性,而不是执行正常的属性检索obj.__special__,并调用它。基本原理是 obj 的实例属性可能需要接收器参数(通常称为 self),它是 obj 要调用的实例(例如函数属性) 而 special(obj, *args, **kwargs) 不提供一个,这与 obj 的类属性相反,它可能需要一个接收器参数(通常称为 self),它是类 {{1} 的一个实例} 被调用(例如函数属性)和type(obj)提供了一个:special(obj, *args, **kwargs)

示例

特殊方法 obj 接受单个参数。比较这两个表达式:

__hash__
  • 第一个表达式从 class 属性 >>> 1 .__hash__ <method-wrapper '__hash__' of int object at 0x103c1f930> >>> int.__hash__ <slot wrapper '__hash__' of 'int' objects> 中检索绑定到 vars(type(1))['__hash__'].__get__(1) 的方法 1。所以 class 属性需要一个接收器参数,它是要调用的 vars(type(1))['__hash__'] 的一个实例,我们已经提供了一个:type(1)
  • 第二个表达式从 instance 属性 1 中检索函数 vars(int)['__hash__'].__get__(None, int)。所以实例属性需要一个接收器参数,它是要调用的 vars(int)['__hash__'] 的一个实例,我们还没有提供。
int

由于内置函数 >>> 1 .__hash__() 1 >>> int.__hash__(1) 1 采用单个参数,hash 可以提供第一次调用(类属性调用)所需的 hash(1)1 不能提供第二次调用(实例属性调用)中所需的 hash(int)。因此,1 应该绕过实例属性 hash(obj) 并直接访问类属性 vars(obj)['__hash__']

vars(type(obj))['__hash__']