"特殊方法的隐式使用总是依赖于特殊方法的类级绑定"

时间:2017-09-05 11:45:27

标签: python

我很难理解来自Python的Nutshell中的最后一部分(粗体)

  

每实例方法

     

实例可以为所有属性设置特定于实例的绑定,   包括可调用属性(方法)。对于一种方法,就像   任何其他属性(除了那些绑定到覆盖描述符的属性),   特定于实例的绑定隐藏了类级绑定:   属性查找在找到时不考虑该类   直接绑定在实例中。特定于实例的绑定   callable属性不执行任何转换   详见“绑定和未绑定方法”(第110页):属性   reference返回与之前相同的可调用对象   直接绑定到实例属性。

     

然而,这并不像您预期​​的那样有效   对于Python调用的特殊方法的每实例绑定   隐含地由于各种操作,如“特别报告”所述   方法“第123页。特殊方法的这种隐式使用总是如此   依赖于特殊方法的类级绑定(如果有的话)。 For   例如:

def fake_get_item(idx): return idx
class MyClass(object): pass
n = MyClass()
n.__getitem__ = fake_get_item
print(n[23])                      # results in:
# Traceback (most recent call last):
#   File "<stdin>", line 1, in ?
# TypeError: unindexable object

具体是什么意思?

为什么示例的错误?

感谢。

3 个答案:

答案 0 :(得分:6)

忽略所有精细细节它基本上说明了特殊方法(如Pythons data model中所定义 - 通常这些方法以两个下划线开头,以两个下划线结尾,很少,如果有的话,直接调用)将< strong>从不从实例中使用隐式,即使在那里定义:

n[whatever]  # will always call type(n).__getitem__(n, whatever)

这与首先检查实例的属性查找不同:

def fake_get_item(idx): 
    return idx

class MyClass(object): 
    pass

n = MyClass()
n.__getitem__ = fake_get_item
print(n.__getitem__(23))     # works because attribute lookup checks the instance first

文档中有关于此的整个部分(包括基本原理):"Special method lookup"

  

3.3.9。特殊方法查找

     

对于自定义类,只有在对象的类型上定义,而不是在对象的实例字典中,才能保证特殊方法的隐式调用正常工作。这种行为是以下代码引发异常的原因:

>>> class C:
...     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
     

[...]

     

以这种方式绕过__getattribute__()机制为解释器内的速度优化提供了很大的空间,代价是处理特殊方法的一些灵活性(必须在类对象本身上按顺序设置特殊方法由翻译一致地调用。)

答案 1 :(得分:2)

更明确地说,这意味着您无法动态重新定义dunder方法。因此,==+和其他运算符对于T类型的所有对象始终具有相同的含义。

答案 2 :(得分:2)

我将尝试总结提取物所说的内容,特别是粗体部分。 一般来说,当Python尝试查找属性(包括方法)的值时,它首先检查实例(即您创建的实际对象),然后检查类。 下面的代码说明了一般行为。

class MyClass(object):
    def a(self):
        print("howdy from the class")

n = MyClass()
#here the class method is called
n.a()
#'howdy from the class'

def new_a():
    print("hello from new a")    
n.a = new_a
#the new instance binding hides the class binding
n.a()
#'hello from new a'

粗体声明的部分是适用于__getitem__等“特殊方法”。换句话说,在实例级别覆盖__getitem__(在示例中为n.__getitem__ = fake_get_item)什么都不做:当通过n[]语法调用该方法时,会引发错误,因为类没有实施方法。 (如果在这种情况下也保持通用行为,则print(n[23])的结果将是打印23,即执行fake_get_item方法。

同一行为的另一个例子:

class MyClass(object):
    def __getitem__(self, idx):
        return idx
n = MyClass()
fake_get_item = lambda x: "fake"
print(fake_get_item(23))
#'fake'
n.__getitem__ = fake_get_item
print(n[23])
#'23'

在此示例中,将调用__getitem__方法(返回索引号),而不是实例绑定(返回'fake')。