如何将__getattr__的这个例子理解为元类方法

时间:2016-06-15 04:48:44

标签: python

以下是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

我有以下问题:

  1. 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;

  2. B.upper走向<built-in method upper of str object at 0x029E7420>的路径。它是否也通过getattr,参数的真正价值是什么?

  3. B.append(4)是否通过A.__getattr__(cls, name)?如果是这样,在这种情况下getattr(cls.data,name)中参数的真值是什么?

  4. B.__getitem__(0)如何产生1?在这种情况下,getattr(cls.data,name)中参数的真值是什么?

2 个答案:

答案 0 :(得分:2)

  1. B.upper()首先查找B.upper,然后不带参数调用它。通过按特定顺序尝试多个选项来查找B.upper,最终尝试type(B).__getattr__(B, 'upper'),在这种情况下为A.__getattr__(B, 'upper'),返回'spam'.upper
  2. 如上所述,B.upper会经历多个选项,在这种情况下会达到type(B).__getattr__(B, 'upper') A.__getattr__(B, 'upper')
  3. 是的,在这种情况下,B.append将会A.__getattr__(B, 'append')到达B.data.append
  4. 在这种情况下,
  5. B.__getitem__(0)会查找B.__getitem__,并通过A.__getattr__(B, '__getitem__')找到它,它将返回B.data.__getitem__
  6. 另请注意,最后一个示例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}}上访问它索引失败。

  1. 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;
  2. __getitem__没有任何意义,属性的名称始终是一个字符串,所以使用getattr('spam', upper())(尚未调用)对B.upper来说是等价的,那么你调用该方法

    1. B.upper走路的路径是什么。它是通过getattr,是什么 参数的真实价值?
    2. 你有什么理由不只是添加一个打印语句来检查吗?

      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>