Python中的元类是什么?

7323

什么是元类?它们有什么用途?

25个回答

11

我在一个名为 classutilities 的包中看到了一种有趣的元类用法。它检查所有类变量是否都是大写格式(这样对于配置类来说具有统一逻辑很方便),并检查类中是否没有实例级别的方法。

另一个有趣的元类示例是基于复杂条件(检查多个环境变量的值)来禁用单元测试。


10

在Python中,元类是一个子类的子类,它决定了子类的行为方式。一个类是另一个元类的实例。在Python中,一个类指定类的实例将如何表现。

由于元类负责生成类,因此您可以编写自己的自定义元类来通过执行附加操作或注入代码来更改创建类的方式。自定义元类并不总是重要的,但它们可能是。


2

看这个:

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'>)
>>> 

换句话说,当一个对象没有被创建(即对象类型不存在)时,我们会查找MetaClass。

2

我想补充一下为什么要使用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'}
  1. type.__new__MyMeta 赋值给了 AClass.__class__

    如何实现的呢?type.__new__ 接受第一个参数 cls,也就是 MyMeta,并执行 AClass.__class__ = MyMeta

    当我们试图创建 SubAClass 作为 AClass 的子类时,Python 会查看我们指定用于创建 SubAClass 的元类,在这种情况下,我们没有传递元类给 SubAClass,因此 Python 得到了 None 作为元类。

    然后 Python 会尝试获取 SubAClass 的第一个基类的元类,显然它得到了 MyMeta

  2. 如果你调用 type() 而不是 type.__new__,那么我们将会得到 AClass.__class__type。为什么呢?

    type() 仍然调用 type.__new__,但隐式地将 type 作为第一个参数传递。

    这意味着 AClass 等同于 BClass,它们都有 type 作为它们的 __class__ 属性。

在C代码中,元类的搜索是如何工作的?

它的工作方式基本上就像我们刚刚提到的那样

当您定义一个类时,函数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__

0
在Python或任何其他语言中,我们为声明的每个变量或对象都有一个类型。要获取Python中任何内容(变量、对象等)的类型,我们可以使用type()函数。
通过在类定义中绕过元类关键字,我们可以自定义类创建过程。
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))

在定义一个新类时,如果没有定义元类,则使用默认的类型元类。如果给定的元类不是type()的对象(实例),则直接将其作为元类使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接