【问题标题】:Why does a class have the attributes of its metaclass?为什么一个类具有它的元类的属性?
【发布时间】:2016-02-02 01:46:35
【问题描述】:

我有一个这样的元类

class UpperMeta(type):
    def __new__(cls, clsname, bases, dct):
        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperMeta, cls).__new__(cls, clsname, bases, uppercase_attr)

    def echo(cls):
        return 'echo'

class B(object):
    __metaclass__ = UpperMeta

assert hasattr(B, 'echo')
assert B.echo() == 'echo'
assert not issubclass(B, UpperMeta)

我的问题是:

  1. 为什么B 类有echo 方法?如果B 不是UpperMeta 的子类,它不应该有echo 属性吗?
  2. 类从元类中获得哪些属性?

【问题讨论】:

    标签: python subclass metaclass


    【解决方案1】:

    class 对象 B 属于 type UpperMeta

    这导致UpperMeta 的所有类方法在类B 上都可用。该属性不在B 类上,而是从B 的类代理(B 是类,而不是B 的实例)

    >>> print dir(B)
    # General lack of echo()
    ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
    

    在这里:

    >>> print dir(B.__class__)
    ['__abstractmethods__', '__base__',  ..., 'echo', 'mro']
    

    来自documentation

    属性访问的默认行为是获取、设置或删除 对象字典中的属性。例如, a.x 有一个 以 a.dict['x'] 开头的查找链,然后 type(a).dict['x'],并继续遍历 type(a) 不包括元类。


    这有点令人困惑“...不包括元类..”,这实际上意味着类型(a)的元类,因此说如果你的元类UpperMeta有一个元类TopMetaTopMeta 定义sos(),不会被查找:

    class TopMeta(type):
        def __new__(cls, clsname, bases, dct):
            uppercase_attr = {}
            for name, val in dct.items():
                if not name.startswith('__'):
                    uppercase_attr[name.upper()] = val
                else:
                    uppercase_attr[name] = val
    
            return super(TopMeta, cls).__new__(cls, clsname, bases, uppercase_attr)
    
        def sos(cls):
            return 'sos'
    
    class UpperMeta(type):
        __metaclass__ = TopMeta
        def __new__(cls, clsname, bases, dct):
            uppercase_attr = {}
            for name, val in dct.items():
                if not name.startswith('__'):
                    uppercase_attr[name.upper()] = val
                else:
                    uppercase_attr[name] = val
    
            return super(UpperMeta, cls).__new__(cls, clsname, bases, uppercase_attr)
    
    class B(object):
        __metaclass__ = UpperMeta
    
    assert not hasattr(B, 'sos')
    

    唯一正确解释元类的演讲:David Beazley - Python 3 Metaprogramming。你只有前 80 分钟左右。

    【讨论】:

      【解决方案2】:

      为什么B类有echo的方法,B不是UpperMeta的子类,它 不应该有 echo attr 吗?

      如果您查看 What is a metaclass in Python?Customizing class creation,您会看到(引用自 python 文档)

      如果定义了__metaclass__,那么分配给它的可调用对象将是 调用而不是 type()。

      type()

      本质上是类语句的动态形式。名称字符串 是类名并成为__name__ 属性;基地 tuple 逐项列出基类并成为__bases__ 属性; 并且 dict 字典是包含定义的命名空间 类主体并成为__dict__ 属性。例如, 以下两个语句创建相同类型的对象:

      >>> class X(object):
      ...     a = 1
      ...
      >>> X = type('X', (object,), dict(a=1))
      

      这是,它“就像”类扩展。这也回答了

      从元类中得到什么属性类?

      几乎所有东西。

      也请be aware that

      如果你想知道自己是否需要它们,你就不需要(那些 实际上需要他们确定地知道他们需要他们,而不是 需要解释原因)。

      但在video posted by @Sebastian,他说

      问:你会不会有太多的 [元编程] 答:没有

      所以他认为学习很重要。

      【讨论】:

      • 我找不到报价!谢谢。
      • @Sebastian:不客气。就在开始之后。谢谢你的视频。
      猜你喜欢
      • 1970-01-01
      • 2021-12-04
      • 2015-06-27
      • 2019-09-05
      • 1970-01-01
      • 2020-09-09
      • 2014-12-05
      • 2014-06-09
      • 2012-07-17
      相关资源
      最近更新 更多