为啥一个类具有它的元类的属性?

Posted

技术标签:

【中文标题】为啥一个类具有它的元类的属性?【英文标题】: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)

我的问题是:

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

【问题讨论】:

【参考方案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:不客气。就在开始之后。谢谢你的视频。

以上是关于为啥一个类具有它的元类的属性?的主要内容,如果未能解决你的问题,请参考以下文章

从它的元类python引用一个类的实例

具有自定义元类的类的所有子类共享相同的属性,即使它们不应该

为啥元类不能访问由元类定义的类的子类继承的属性?

TypeError:元类冲突:派生类的元类必须是其所有基类的元类的(非严格)子类

在 Groovy 中,实例的元类与其类的元类有啥区别

理解python的元类