Click to Visit Homepage : zzyzz.top


对特殊方法的访问 - Special method lookup

 1 对特殊方法的访问 - Special method lookup
 2 
 3     对于用户自定义的 class 来说, 特殊方法只有通过定义对象的类型object’s type (而非通过 instance
 4__dict__属性)被定义, 才能保证特殊方法的隐式调用.
 5     也就是说给自定义的 class 打 特殊方法的 monkey patching 后,再通过 conventional'传统方式' 调用是行不通的.
 6         注, conventional'传统方式' 调用 指的是如下面例子中 len() 跟 __len__() 的对应关系.
 7     见下例的 exception,
 8         例,
 9             >>> class C:
10             ...     pass
11             ...
12             >>> c = C()
13             >>> c.__len__ = lambda: 5
14             >>> c.__len__()
15             5
16             >>> len(c)
17             Traceback (most recent call last):
18               File "<stdin>", line 1, in <module>
19             TypeError: object of type 'C' has no len()
20 
21         报错的原因在于, 所有对象(包括类型对象) 都有实现特殊方法, 如果通过'传统方式'隐式调用
22         这些特殊方法(len(c)), 当在类型对象本身上调用对应的特殊方法的时候便会失败.
23         详见 python doc 中的描述,
24             The rationale behind this behaviour lies with a number of special methods such as __hash__() and __repr__()
25             that are implemented by all objects, including type objects.
26             If the implicit lookup of these methods used the conventional lookup process,
27             they would fail when invoked on the type object itself:
28 
29         例如,
30             >>> 1 .__hash__() == hash(1)
31             True
32             >>> int.__hash__() == hash(int)
33             Traceback (most recent call last):
34               File "<stdin>", line 1, in <module>
35             TypeError: descriptor '__hash__' of 'int' object needs an argument
36 
37             错误在于, 试图去调用一个类(int) 的 unbound method(__hash__), 有时这样的错误调用行为
38             被称作‘metaclass confusion’. 可以采取通过 instance 访问 special methods
39             的方式绕过 ‘metaclass confusion’.
40             >>> type(1).__hash__(1) == hash(1)
41             True
42             >>> type(int).__hash__(int) == hash(int)
43             True
44 
45             这样做的另外的一个好处是, 对特殊方法的隐式调用同时也绕过 __getattribute__ 方法,
46             对该对象的的 metaclass 来说也是这样.
47             >>> class Meta(type):
48             ...     def __getattribute__(*args):
49             ...         print("Metaclass getattribute invoked")
50             ...         return type.__getattribute__(*args)
51             ...
52             >>> class C(object, metaclass=Meta):
53             ...     def __len__(self):
54             ...         return 10
55             ...     def __getattribute__(*args):
56             ...         print("Class getattribute invoked")
57             ...         return object.__getattribute__(*args)
58             ...
59             >>> c = C()
60             >>> c.__len__()                 # Explicit lookup via instance
61             Class getattribute invoked
62             10
63             >>> type(c).__len__(c)          # Explicit lookup via type
64             Metaclass getattribute invoked
65             10
66             >>> len(c)                      # Implicit lookup
67             10
68 
69     summarize,
70         '绕过' __getattribute__ 的机制最佳化了解释器的解释速度, 而这种机制是通过对特殊方法的灵活处理
71         实现的,即特殊方法必须在类对象本身上 set, 而不能通过 monkey patching,再通过 conventional'传统方式' 调用.
72 
73         注,
74             conventional'传统方式' 调用 指的是诸如例子中 len() 跟 __len__() 的对应关系.
75 
76     reference,
77         python doc,
78             https://docs.python.org/3/reference/datamodel.html#special-lookup

 

posted @ 2017-10-27 15:44  zzYzz  阅读(269)  评论(0编辑  收藏  举报


Click to Visit Homepage : zzyzz.top