为什么 class 具有其元 class 的属性?

Why does a class have the attributes of its metaclass?

我有一个像这样的 metaclass

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. 为什么 class B 有一个 echo 方法?如果 B 不是 UpperMeta 的子 class,它不应该有 echo 属性?
  2. class 从元class 中获得了哪些属性?

class 对象 B 属于 type UpperMeta

这导致 UpperMeta 的所有 class 方法在 class B 上可用。该属性不在 class B 上,而是从 B 的 class 代理(B 是 class,不是实例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:

The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting with a.dict['x'], then type(a).dict['x'], and continuing through the base classes of type(a) excluding metaclasses.


结尾有点混乱"...不包括元classes..",这实际上意味着元classes类型(a),因此说如果你的元class UpperMeta有一个元class 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')

唯一正确解释 metaclasses 的演讲:David Beazley - Python 3 Metaprogramming。您只有前 80 分钟左右的时间。

why class B have method of echo, B is not subclass of UpperMeta, it should not have echo attr?

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

if __metaclass__ is defined then the callable assigned to it will be called instead of type().

type()

is essentially a dynamic form of the class statement. The name string is the class name and becomes the __name__ attribute; the bases tuple itemizes the base classes and becomes the __bases__ attribute; and the dict dictionary is the namespace containing definitions for class body and becomes the __dict__ attribute. For example, the following two statements create identical type objects:

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

这是,它是"just like" class扩展名。这也回答了

What attribute class get from metaclass?

几乎所有内容。

也请be aware that

If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

然而在,他说

Q: Can you have too much of [metaprogramming] A: No

所以他觉得学习很重要