以下是Mark Lutz的书" Learning Python"中的一个例子。我发现很难理解如何将名称访问转换为元类中的getattr()调用:
>>> class A(type):
def __getattr__(cls, name):
return getattr(cls.data, name)
>>> class B(metaclass=A):
data = 'spam'
>>> B.upper()
'SPAM'
>>> B.upper
<built-in method upper of str object at 0x029E7420>
>>> B.__getattr__
<bound method A.__getattr__ of <class '__main__.B'>>
>>> B.data = [1, 2, 3]
>>> B.append(4)
>>> B.data
[1, 2, 3, 4]
>>> B.__getitem__(0)
1
>>> B[0]
TypeError: 'A' object does not support indexing
我有以下问题:
B.upper()如何产生&#39; SPAM&#39;?是因为B.upper()=&gt; A.__getattr__(B, upper())
=&gt; getattr(B.data,upper())?但像getattr(&#39; spam&#39;,upper())这样的调用会出现错误&#34; NameError:name&#39; upper&#39;未定义&#34;
B.upper走向<built-in method upper of str object at 0x029E7420>
的路径。它是否也通过getattr,参数的真正价值是什么?
B.append(4)是否通过A.__getattr__(cls, name)
?如果是这样,在这种情况下getattr(cls.data,name)中参数的真值是什么?
B.__getitem__(0)
如何产生1?在这种情况下,getattr(cls.data,name)中参数的真值是什么?
答案 0 :(得分:2)
B.upper()
首先查找B.upper
,然后不带参数调用它。通过按特定顺序尝试多个选项来查找B.upper
,最终尝试type(B).__getattr__(B, 'upper')
,在这种情况下为A.__getattr__(B, 'upper')
,返回'spam'.upper
。B.upper
会经历多个选项,在这种情况下会达到type(B).__getattr__(B, 'upper')
A.__getattr__(B, 'upper')
。B.append
将会A.__getattr__(B, 'append')
到达B.data.append
。B.__getitem__(0)
会查找B.__getitem__
,并通过A.__getattr__(B, '__getitem__')
找到它,它将返回B.data.__getitem__
。另请注意,最后一个示例B[0]
不起作用,因为B
类不直接定义__getitem__
方法。这是因为“特殊”方法(例如__getitem__
)在通过其特殊语法(例如B[0]
)使用时会以不同方式查找。
答案 1 :(得分:1)
首先,您不需要添加元类的常见混淆来获得此行为,您可以轻松地使用常规类和实例作为示例:
class A():
def __getattr__(cls, name):
return getattr(cls.data, name)
B = A()
B.data = "spam"
>>> B.data
'spam'
>>> B.upper
<built-in method upper of str object at 0x1057da730>
>>> B.upper()
'SPAM'
>>> B.__getitem__
<method-wrapper '__getitem__' of str object at 0x1057da730>
>>> B.__getitem__(0)
's'
>>> B[0]
Traceback (most recent call last):
File "<pyshell#135>", line 1, in <module>
B[0]
TypeError: 'A' object does not support indexing
接下来请注意B[0]
没有使用您的特殊方法查找B.__getitem__
,而是尝试直接在没有type(B)
的{{1}}上访问它索引失败。
__getitem__
没有任何意义,属性的名称始终是一个字符串,所以使用getattr('spam', upper())
(尚未调用)对B.upper
来说是等价的,那么你调用该方法
你有什么理由不只是添加一个打印语句来检查吗?
getattr(B, "upper")
通过添加此print语句来回答3和4:
class A():
def __getattr__(cls, name):
print("calling __getattr__ for this name: %r"%name)
return getattr(cls.data, name)
>>> B = A()
>>> B.data = "spam"
>>> B.upper
calling __getattr__ for this name: 'upper'
<built-in method upper of str object at 0x1058037a0>