我很难理解来自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
具体是什么意思?
为什么示例的错误?
感谢。
答案 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'
)。