什么是元类?它们有什么用途?
我在一个名为 classutilities
的包中看到了一种有趣的元类用法。它检查所有类变量是否都是大写格式(这样对于配置类来说具有统一逻辑很方便),并检查类中是否没有实例级别的方法。
另一个有趣的元类示例是基于复杂条件(检查多个环境变量的值)来禁用单元测试。
在Python中,元类是一个子类的子类,它决定了子类的行为方式。一个类是另一个元类的实例。在Python中,一个类指定类的实例将如何表现。
由于元类负责生成类,因此您可以编写自己的自定义元类来通过执行附加操作或注入代码来更改创建类的方式。自定义元类并不总是重要的,但它们可能是。
看这个:
Python 3.10.0rc2 (tags/v3.10.0rc2:839d789, Sep 7 2021, 18:51:45) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> class Object:
... pass
...
>>> class Meta(type):
... test = 'Worked!!!'
... def __repr__(self):
... return 'This is "Meta" metaclass'
...
>>> class ObjectWithMetaClass(metaclass=Meta):
... pass
...
>>> Object or type(Object())
<class '__main__.Object'>
>>> ObjectWithMetaClass or type(ObjectWithMetaClass())
This is "Meta" metaclass
>>> Object.test
AttributeError: ...
>>> ObjectWithMetaClass.test
'Worked!!!'
>>> type(Object)
<class 'type'>
>>> type(ObjectWithMetaClass)
<class '__main__.Meta'>
>>> type(type(ObjectWithMetaClass))
<class 'type'>
>>> Object.__bases__
(<class 'object'>,)
>>> ObjectWithMetaClass.__bases__
(<class 'object'>,)
>>> type(ObjectWithMetaClass).__bases__
(<class 'type'>,)
>>> Object.__mro__
(<class '__main__.Object'>, <class 'object'>)
>>> ObjectWithMetaClass.__mro__
(This is "Meta" metaclass, <class 'object'>)
>>>
我想补充一下为什么要使用type.__new__()
而不是type()
首先,看一下以下类:
In [1]: class MyMeta(type):
...: def __new__(cls, cls_name, bases, attrs):
...: print(cls, cls_name, bases, attrs)
...: return super().__new__(cls, cls_name, bases, attrs)
...:
In [2]: class AClass(metaclass=MyMeta):
...: pass
...:
<class '__main__.MyMeta'> AClass () {'__module__': '__main__', '__qualname__': 'AClass'}
In [3]: class BClass:
...: pass
...:
In [4]: AClass.__class__
Out[4]: __main__.MyMeta
In [5]: BClass.__class__
Out[5]: type
In [6]: class SubAClass(AClass):
...: pass
...:
<class '__main__.MyMeta'> SubAClass (<class '__main__.AClass'>,) {'__module__': '__main__', '__qualname__': 'SubAClass'}
type.__new__
将 MyMeta
赋值给了 AClass.__class__
。
如何实现的呢?type.__new__
接受第一个参数 cls,也就是 MyMeta,并执行 AClass.__class__ = MyMeta
。
当我们试图创建 SubAClass 作为 AClass 的子类时,Python 会查看我们指定用于创建 SubAClass 的元类,在这种情况下,我们没有传递元类给 SubAClass,因此 Python 得到了 None 作为元类。
然后 Python 会尝试获取 SubAClass 的第一个基类的元类,显然它得到了 MyMeta
。
如果你调用 type()
而不是 type.__new__
,那么我们将会得到 AClass.__class__
是 type
。为什么呢?
type()
仍然调用 type.__new__
,但隐式地将 type
作为第一个参数传递。
这意味着 AClass 等同于 BClass,它们都有 type 作为它们的 __class__
属性。
它的工作方式基本上就像我们刚刚提到的那样
当您定义一个类时,函数builtin___build_class__
将会被调用
而且代码非常简单明了
static PyObject *
builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
PyObject *kwnames){
if (meta == NULL) {
/* if there are no bases, use type: */
if (PyTuple_GET_SIZE(bases) == 0) {
meta = (PyObject *) (&PyType_Type);
}
/* else get the type of the first base */
else {
PyObject *base0 = PyTuple_GET_ITEM(bases, 0);
meta = (PyObject *)Py_TYPE(base0);
}
Py_INCREF(meta);
isclass = 1; /* meta is really a class */
}
PyObject *margs[3] = {name, bases, ns};
cls = PyObject_VectorcallDict(meta, margs, 3, mkw);
}
meta = (PyObject *)Py_TYPE(base0);
是我们想知道的一切。meta = Py_TYPE(AClass) = MyMeta = AClass.__class__
。class meta(type):
pass
class baseclass(metaclass=meta): # This is Mestaclass
pass
class derivedclass(baseclass):
pass
print(type(meta))
print(type(baseclass))
print(type(derivedclass))