oop:Python中的元类是什么?

元类是什么?我们使用它们做什么?


类对象

在理解元类之前,您需要掌握Python中的类。Python对类有一个非常独特的概念,它是从Smalltalk语言借来的。

在大多数语言中,类只是描述如何生成对象的代码片段。在Python中也是这样:

1
2
3
4
5
6
7
>>> class ObjectCreator(object):
...       pass
...

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

但是在Python中,类不仅仅是这样。类也是对象。

是的,对象。

只要使用关键字class, Python就会执行并创建它一个对象。指令

1
2
3
>>> class ObjectCreator(object):
...       pass
...

在内存中创建一个名为"ObjectCreator"的对象。

这个对象(类)本身能够创建对象(实例),这就是为什么它是一个类。

但它仍然是一个物体,因此:

你可以把它赋值给一个变量你可以复制您可以向它添加属性您可以将它作为函数参数传递

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

动态创建类

因为类是对象,所以您可以像创建任何对象一样动态地创建它们。

首先,您可以使用class在函数中创建一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

但它不是动态的,因为你仍然需要自己编写整个类。

由于类是对象,它们必须由某些东西生成。

当您使用class关键字时,Python会自动创建这个对象。但是,随着对于Python中的大多数东西,它提供了一种手动执行的方法。

还记得函数type吗?让你知道什么的老函数对象类型为:

1
2
3
4
5
6
7
8
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

嗯,type有一个完全不同的功能,它还可以动态创建类。type可以将类的描述作为参数,并返回一个类。

(我知道,根据传递给它的参数,同一个函数可以有两种完全不同的用途,这很愚蠢。这是一个向后的问题兼容在Python中)

type是这样工作的:

1
2
3
type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

例如:

1
2
>>> class MyShinyClass(object):
...       pass

可以手动创建如下:

1
2
3
4
5
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

您将注意到,我们使用"MyShinyClass"作为类的名称并作为保存类引用的变量。它们可以是不同的,但没有理由把事情复杂化。

type接受一个字典来定义类的属性。所以:

1
2
>>> class Foo(object):
...       bar = True

可译为:

1
>>> Foo = type('Foo', (), {'bar':True})

并用作普通类:

1
2
3
4
5
6
7
8
9
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

当然,你可以继承它,所以:

1
2
>>>   class FooChild(Foo):
...         pass

是:

1
2
3
4
5
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

最终,您将希望向类添加方法。定义一个函数使用适当的签名并将其作为属性分配。

1
2
3
4
5
6
7
8
9
10
11
>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

在动态创建类之后,还可以添加更多的方法,就像向通常创建的类对象添加方法一样。

1
2
3
4
5
6
>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

在Python中,类是对象,您可以动态地动态地创建一个类。

这就是Python在使用关键字class时所做的,它是通过使用元类来做的。

什么是元类(最终)

元类是创建类的"东西"。

定义类是为了创建对象,对吧?

但是我们知道Python类是对象。

元类是创建这些对象的。他们是班级的班级,你可以这样想象:

1
2
MyClass = MetaClass()
my_object = MyClass()

您已经看到type允许您这样做:

1
MyClass = type('MyClass', (), {})

这是因为函数type实际上是一个元类。type是元类Python用于在后台创建所有类。

现在您想知道为什么它是小写的,而不是Type?

所有东西,我是说所有东西,都是Python中的对象。包括整数、字符串、函数和类。它们都是对象。而且他们都做到了从一个类创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

现在,任何一个__class____class__是什么?

1
2
3
4
5
6
7
8
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

元类就是创建类对象的东西。

如果你愿意,你可以称它为"类工厂"。

type是Python使用的内置元类,但是当然,您可以创建您的自己的元类。

__metaclass__属性

在python2中,您可以在编写类时添加__metaclass__属性(有关python3语法,请参阅下一节):

1
2
3
class Foo(object):
    __metaclass__ = something...
    [...]

如果这样做,Python将使用元类创建类Foo

小心,这是棘手。

首先编写class Foo(object),但是没有创建类对象Foo在内存中。

Python将在类定义中查找__metaclass__。如果它找到了,它将使用它来创建对象类Foo。如果没有,它就会使用type创建类。

读几遍。

当你做的事:

1
2
class Foo(Bar):
    pass

Python执行以下操作:

Foo中是否有一个__metaclass__属性?

如果是,在内存中创建一个类对象(我说的是类对象,请留在这里),并使用__metaclass__中的名称Foo

如果Python找不到__metaclass__,它将在模块级别上查找一个__metaclass__,并尝试执行相同的操作(但仅针对不继承任何东西的类,基本上是旧式类)。

然后,如果它根本找不到任何__metaclass__,它将使用Bar的(第一个父类)自己的元类(可能是默认的type)来创建类对象。

这里要注意,__metaclass__属性不会被继承,父类的元类(Bar.__class__)将被继承。如果Bar使用__metaclass__属性创建了Bartype()(而不是type.__new__()),子类将不会继承该行为。

现在最大的问题是,你能在__metaclass__中放入什么?

答案是:可以创建类的东西。

什么可以创建类?type,或任何子类或使用它的东西。

< Python 3中的hh1>元类

在Python 3中,设置元类的语法已经更改:

1
2
class Foo(object, metaclass=something):
    ...

也就是说,不再使用__metaclass__属性,而是使用基类列表中的关键字参数。

然而元类的行为在很大程度上保持不变。

python 3中向元类添加的一件事是,您还可以将属性作为关键字参数传递到元类中,如下所示:

1
2
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
    ...

阅读下面的部分了解python如何处理这个问题。

自定义元类

元类的主要目的是自动改变类,当它创建。

您通常对api这样做,在api中,您希望创建与当前上下文。

想象一个愚蠢的例子,你决定所有的类在你的模块它们的属性应该是大写的。有几种方法这样做,但是一种方法是在模块级设置__metaclass__

这样,这个模块的所有类都将使用这个元类创建,我们只需要告诉元类把所有属性都变成大写。

幸运的是,__metaclass__实际上可以是任何可调用的,它不需要是a正式的类(我知道,名称中有class的东西不需要是正式的类一个类,去想想……但是这是有帮助的)。

我们从一个简单的例子开始,用一个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
   """
      Return a class object, with the list of its attribute turned
      into uppercase.
   """


    # pick up any attribute that doesn't start with '__' and uppercase it
    uppercase_attr = {}
    for name, val in future_class_attr.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

    # let `type` do the class creation
    return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with"object" though
    # but we can define __metaclass__ here instead to affect only this class
    # and this will work with"object" children
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: 'bip'

现在,让我们做同样的事情,但是对元类使用一个真实的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

但这并不是真正的OOP。我们直接调用type,而不覆盖它或者调用父类__new__。让我们做到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

您可能已经注意到了额外的参数upperattr_metaclass。有它没有什么特别的:__new__总是接收它定义的类,作为第一个参数。就像您为普通方法使用self一样,这些方法接收实例作为第一个参数,或者为类方法定义类。

当然,为了清楚起见,我在这里使用的名称很长,但是就像对于self,所有参数都有常规名称。所以真正的生产元类应该是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
class UpperAttrMetaclass(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 type.__new__(cls, clsname, bases, uppercase_attr)

我们可以使用super使它更简洁,这将简化继承(因为是的,您可以有元类,从元类继承,从类型继承):

1
2
3
4
5
6
7
8
9
10
11
12
class UpperAttrMetaclass(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(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

哦,在python3中,如果你用关键字参数做这个调用,像这样:

1
2
class Foo(object, metaclass=Thing, kwarg1=value1):
    ...

它在元类中转换为:

1
2
3
class Thing(type):
    def __new__(class, clsname, bases, dct, kwargs1=default):
        ...

就是这样。关于元类真的没有更多的内容了。

使用元类的代码之所以复杂,并不是因为对于元类,这是因为您通常使用元类来做一些扭曲的事情依赖于内省、操作继承、var(例如__dict__)等。

的确,元类对黑魔法特别有用,因此复杂的东西。但就其本身而言,它们很简单:

拦截类创建修改的类返回修改后的类为什么要使用元类而不是函数?

既然__metaclass__可以接受任何可调用的类,为什么要使用类呢因为它显然更复杂?

这样做有几个原因:

其意图是明确的。当你阅读UpperAttrMetaclass(type)时,你就知道了接下来会发生什么您可以使用OOP。元类可以继承自元类,覆盖父方法。元类甚至可以使用元类。如果您指定了一个元类,但没有指定一个元类函数,则类的子类将是其元类的实例。您可以更好地构造代码。您永远不会将元类用于某些as就像上面的例子一样简单。通常是为了一些复杂的东西。有能够创建多个方法并将它们分组到一个类中是非常有用的使代码更容易阅读。您可以挂接__new____init____call__。这将使你要做不同的事情。即使你通常可以在__new__中完成所有的操作,有些人只是更习惯使用__init__。这些叫做元类,该死!一定有什么意思!为什么要使用元类?

现在有个大问题。为什么要使用一些容易出错的模糊特性?

通常你不会:

Metaclasses are deeper magic that
99% of users should never worry about.
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).

巨蟒大师蒂姆·彼得斯

元类的主要用例是创建API。Django ORM就是一个典型的例子。

它允许你这样定义:

1
2
3
class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

但如果你这样做:

1
2
guy = Person(name='bob', age='35')
print(guy.age)

它不会返回IntegerField对象。它将返回一个int,甚至可以直接从数据库中获取它。

这是可能的,因为models.Model定义了__metaclass__和它使用了一些魔法,可以将您刚才用简单语句定义的Person变为Person连接到数据库字段的复杂钩子。

Django通过公开一个简单的API使复杂的东西看起来很简单使用元类,从这个API重新创建代码来完成真正的工作在幕后。

最后一句话

首先,您知道类是可以创建实例的对象。

实际上,类本身就是实例。元类。

1
2
3
>>> class Foo(object): pass
>>> id(Foo)
142630324

在Python中,所有对象都是对象,它们都是类的实例或者元类的实例。

除了type

type实际上是它自己的元类。这不是你能做到的在纯Python中进行复制,并通过在实现中作弊来完成的水平。

其次,元类是复杂的。您可能不想使用它们非常简单的类更改。您可以使用两种不同的技术来更改类:

猴子打补丁类的修饰符

99%的时候你需要修改类,你最好使用这些。

但是98%的时候,你根本不需要修改类。


元类是类的类。类定义类的实例(即对象)的行为方式,而元类定义类的行为方式。类是元类的实例。

虽然在Python中可以为元类使用任意的可调用项(如Jerub所示),但更好的方法是让它本身成为一个实际的类。type是Python中常用的元类。type本身是一个类,它是自己的类型。您不能完全用Python重新创建像type这样的东西,但是Python有一点欺骗。要在Python中创建自己的元类,只需子类type

元类通常用作类工厂。当您通过调用类来创建对象时,Python通过调用元类来创建一个新类(当它执行"class"语句时)。因此,与普通的__init____new__方法相结合,元类允许您在创建类时做"额外的事情",比如用某个注册表注册新类,或者完全用其他方法替换类。

当执行class语句时,Python首先将class语句的主体作为普通代码块执行。结果的名称空间(dict)包含了将来类的属性。元类是通过查看将来类(元类是继承的)的基类、将来类(如果有的话)的__metaclass__属性或__metaclass__全局变量来确定的。然后使用类的名称、基和属性调用元类来实例化它。

然而,元类实际上定义了类的类型,而不仅仅是类的工厂,所以您可以使用它们做更多的事情。例如,您可以在元类上定义普通方法。这些元类方法类似于类方法,因为它们可以在没有实例的情况下在类上调用,但是它们也不像类方法,因为它们不能在类的实例上调用。type.__subclasses__()type元类上方法的一个例子。您还可以定义常规的"魔法"方法,如__add____iter____getattr__,以实现或更改类的行为方式。

下面是一个碎片的聚合示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def make_hook(f):
   """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(mcls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print"Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print"Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print"NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__


注意,这个答案是针对python2的。正如2008年所写的,元类在3中略有不同。x,见注释。

元类是让"类"工作的秘密武器。新样式对象的默认元类称为"type"。

1
2
3
class type(object)
  |  type(object) -> the object's type
  |  type(name, bases, dict) -> a new type

元类需要3个arg。'name', 'bases'和'dict'

这就是秘密开始的地方。在这个示例类定义中,查找name、base和dict的来源。

1
2
3
4
class ThisIsTheName(Bases, Are, Here):
    All_the_code_here
    def doesIs(create, a):
        dict

让我们定义一个元类来演示class:如何调用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def test_metaclass(name, bases, dict):
    print 'The Class Name is', name
    print 'The Class Bases are', bases
    print 'The dict has', len(dict), 'elems, the keys are', dict.keys()

    return"yellow"

class TestName(object, None, int, 1):
    __metaclass__ = test_metaclass
    foo = 1
    def baz(self, arr):
        pass

print 'TestName = ', repr(TestName)

# output =>
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName =  'yellow'

现在,一个实际有意义的例子,这将自动使列表中的变量"属性"设置在类上,并设置为None。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def init_attributes(name, bases, dict):
    if 'attributes' in dict:
        for attr in dict['attributes']:
            dict[attr] = None

    return type(name, bases, dict)

class Initialised(object):
    __metaclass__ = init_attributes
    attributes = ['foo', 'bar', 'baz']

print 'foo =>', Initialised.foo
# output=>
foo => None

注意,通过使用元类init_attributes来"激活"收益的神奇行为不会传递到一个Initalised子类上。

下面是一个更具体的例子,展示了如何子类化"type"来生成一个元类,这个元类在创建类时执行一个操作。这是相当棘手的:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MetaSingleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
        return cls.instance

 class Foo(object):
     __metaclass__ = MetaSingleton

 a = Foo()
 b = Foo()
 assert a is b

元类的一个用途是自动向实例添加新属性和方法。

例如,如果您查看Django模型,它们的定义看起来有点混乱。看起来好像你只定义了类属性:

1
2
3
class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

然而,在运行时,Person对象中充满了各种有用的方法。查看源代码可以找到一些很棒的元代码集。


其他人已经解释了元类是如何工作的,以及它们是如何适应Python类型系统的。下面是一个例子,说明它们的用途。在我编写的一个测试框架中,我希望跟踪类定义的顺序,以便以后可以按照这个顺序实例化它们。我发现使用元类最容易做到这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyMeta(type):

    counter = 0

    def __init__(cls, name, bases, dic):
        type.__init__(cls, name, bases, dic)
        cls._order = MyMeta.counter
        MyMeta.counter += 1

class MyType(object):              # Python 2
    __metaclass__ = MyMeta

class MyType(metaclass=MyMeta):    # Python 3
    pass

任何属于MyType子类的对象都将获得一个class属性_order,该属性记录类的定义顺序。


我认为ONLamp对元类编程的介绍写得很好,尽管已经有好几年的历史了,但是它对这个主题的介绍非常好。

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html(存档于https://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html)

简而言之:类是创建实例的蓝图,元类是创建类的蓝图。可以很容易地看出,在Python类中,也需要是一流的对象才能启用这种行为。

我自己从来没有写过这样的代码,但是我认为元类最好的用法之一可以在Django框架中看到。模型类使用元类方法来支持编写新模型或表单类的声明式风格。当元类创建类时,所有成员都可以自定义类本身。

创建一个新模型启用此功能的元类

剩下要说的是:如果您不知道什么是元类,那么不需要它们的概率是99%。


What are metaclasses? What do you use them for?

TLDR:元类实例化和定义类的行为,就像类实例化和定义实例的行为一样。

伪代码:

1
2
>>> Class(...)
instance

上面这些看起来应该很熟悉。那么,Class是从哪里来的呢?它是元类的一个实例(也是伪代码):

1
2
>>> Metaclass(...)
Class

在实际代码中,我们可以传递默认的元类,type,所有我们需要实例化一个类,我们得到一个类:

1
2
>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>

把它不同

类与实例的关系就像元类与类的关系一样。

当我们实例化一个对象时,我们得到一个实例:

1
2
>>> object()                          # instantiation of class
<object object at 0x7f9069b4e0b0>     # instance

同样,当我们显式地用默认元类type定义一个类时,我们实例化它:

1
2
>>> type('Object', (object,), {})     # instantiation of metaclass
<class '__main__.Object'>             # instance

换句话说,类是元类的一个实例:

1
2
>>> isinstance(object, type)
True

第三种方法是,元类是类的类。

1
2
3
4
>>> type(object) == type
True
>>> object.__class__
<class 'type'>

当您编写类定义并由Python执行时,它使用元类实例化类对象(而类对象又将用于实例化该类的实例)。

正如我们可以使用类定义来更改自定义对象实例的行为方式一样,我们也可以使用元类类定义来更改类对象的行为方式。

它们有什么用呢?从文档:

The potential uses for metaclasses are boundless. Some ideas that have been explored include logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.

然而,通常鼓励用户避免使用元类,除非绝对必要。

每次创建类时都使用元类:

例如,当你写一个类定义时,

1
2
class Foo(object):
    'demo'

实例化一个类对象。

1
2
3
4
>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

它与函数调用type并使用适当的参数并将结果赋给该名称的变量是相同的:

1
2
3
4
name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

注意,有些东西会自动添加到__dict__中,即名称空间:

1
2
3
4
>>> Foo.__dict__
dict_proxy({'__dict__': ,
'__module__': '__main__', '__weakref__': <attribute '__weakref__'
of 'Foo' objects>, '__doc__': 'demo'})

在这两种情况下,我们创建的对象的元类都是type

(附注的内容类__dict__: __module__有因为必须知道它们在哪里定义的类,__dict____weakref__有因为我们不定义__slots__ -如果我们定义__slots__我们会节省一些空间的情况下,我们可以不允许__dict____weakref__排除它们。例如:

1
2
3
>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

…但我离题了。)

我们可以像任何其他类定义一样扩展type:

下面是类的默认__repr__:

1
2
>>> Foo
<class '__main__.Foo'>

默认情况下,我们在编写Python对象时可以做的最有价值的事情之一就是为它提供一个良好的__repr__。当我们调用help(repr)时,我们知道有一个很好的测试__repr__,它也需要一个平等测试—obj == eval(repr(obj))。我们的type类的类实例的__repr____eq__的以下简单实现为我们提供了一个演示,它可以改进默认的__repr__类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Type(type):
    def __repr__(cls):
       """
        >>> Baz
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        >>> eval(repr(Baz))
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
       """

        metaname = type(cls).__name__
        name = cls.__name__
        parents = ', '.join(b.__name__ for b in cls.__bases__)
        if parents:
            parents += ','
        namespace = ', '.join(': '.join(
          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
               for k, v in cls.__dict__.items())
        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
    def __eq__(cls, other):
       """
        >>> Baz == eval(repr(Baz))
        True            
       """

        return (cls.__name__, cls.__bases__, cls.__dict__) == (
                other.__name__, other.__bases__, other.__dict__)

所以现在当我们用这个元类创建一个对象时,命令行上的__repr__提供了一个比默认情况下更美观的视图:

1
2
3
4
>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

通过为类实例定义一个漂亮的__repr__,我们有了更强的调试代码的能力。但是,使用eval(repr(Class))进行更深入的检查是不太可能的(因为函数的默认值__repr__是相当不可能求值的)。

一个预期的用法:__prepare__ a namespace

例如,如果我们想知道类的方法是以什么顺序创建的,我们可以提供一个有序的dict作为类的命名空间。我们将使用__prepare__来实现这一点,如果类是在Python 3中实现的,它将返回类的名称空间dict:

1
2
3
4
5
6
7
8
9
10
from collections import OrderedDict

class OrderedType(Type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return OrderedDict()
    def __new__(cls, name, bases, namespace, **kwargs):
        result = Type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

和用法:

1
2
3
4
5
class OrderedMethodsObject(object, metaclass=OrderedType):
    def method1(self): pass
    def method2(self): pass
    def method3(self): pass
    def method4(self): pass

现在我们有了这些方法(和其他类属性)创建顺序的记录:

1
2
>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

注意,这个例子改编自文档——标准库中的新enum就是这样做的。

所以我们所做的就是通过创建一个类来实例化一个元类。我们也可以像对待其他类一样对待元类。它有一个方法分辨率顺序:

1
2
>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

它大约有正确的repr(我们不能再求它的值,除非我们能找到一种方法来表示我们的函数):

1
2
3
4
>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': , '__doc__': None, '__d
ict__'
: , 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})

Python 3更新

元类中(此时)有两个关键方法:

__prepare____new__

__prepare__允许您提供自定义映射(例如OrderedDict),以便在创建类时用作命名空间。您必须返回您选择的任何名称空间的实例。如果不实现__prepare__,则使用普通的dict

__new__负责最终类的实际创建/修改。

一个简单的、什么都不做的额外元类想要:

1
2
3
4
5
6
7
class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

一个简单的例子:

假设您希望在属性上运行一些简单的验证代码——比如它必须总是intstr。如果没有元类,您的类应该是:

1
2
3
4
class Person:
    weight = ValidateType('weight', int)
    age = ValidateType('age', int)
    name = ValidateType('name', str)

如您所见,您必须重复属性的名称两次。这使得打字错误和烦人的虫子成为可能。

一个简单的元类就可以解决这个问题:

1
2
3
4
class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)

这就是元类的样子(不使用__prepare__,因为它是不需要的):

1
2
3
4
5
6
7
8
9
class Validator(type):
    def __new__(metacls, cls, bases, clsdict):
        # search clsdict looking for ValidateType descriptors
        for name, attr in clsdict.items():
            if isinstance(attr, ValidateType):
                attr.name = name
                attr.attr = '_' + name
        # create final class and return it
        return super().__new__(metacls, cls, bases, clsdict)

样本运行:

1
2
3
4
p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

生产:

1
2
3
4
5
6
7
9
Traceback (most recent call last):
  File"simple_meta.py", line 36, in <module>
    p.weight = '9'
  File"simple_meta.py", line 24, in __set__
    (self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')

注意:这个例子很简单,它也可以通过类装饰器来完成,但是一个实际的元类应该做的更多。

参考的"ValidateType"类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            return inst.__dict__[self.attr]
    def __set__(self, inst, value):
        if not isinstance(value, self.type):
            raise TypeError('%s must be of type(s) %s (got %r)' %
                    (self.name, self.type, value))
        else:
            inst.__dict__[self.attr] = value


元类' __call__()方法在创建类实例时的角色

如果你已经做了几个月的Python编程,你最终会遇到这样的代码:

1
2
3
4
5
6
7
8
9
10
11
# define a class
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

# create an instance of it
instance = SomeClass()

# then call the object as if it's a function
result = instance('foo', 'bar')

当您在类上实现__call__()魔术方法时,后一种方法是可能的。

1
2
3
4
5
6
7
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

当类的实例用作可调用的时调用__call__()方法。但是,正如我们从前面的回答中看到的,类本身是元类的一个实例,所以当我们使用类作为一个可调用的(例如,当我们创建它的一个实例时),我们实际上是在调用它的元类' __call__()方法。此时,大多数Python程序员都有点困惑,因为他们被告知,在创建这样一个instance = SomeClass()实例时,您调用的是它的__init__()方法。一些挖掘得更深的人知道在__init__()之前有__new__()。今天,另一层真相被揭示了,在__new__()之前有元类' __call__()

让我们从创建类实例的角度来研究方法调用链。

这是一个元类,它准确地记录了创建实例之前的时刻以及它将要返回实例的时刻。

1
2
3
4
5
6
class Meta_1(type):
    def __call__(cls):
        print"Meta_1.__call__() before creating an instance of", cls
        instance = super(Meta_1, cls).__call__()
        print"Meta_1.__call__() about to return instance."
        return instance

这是一个使用元类的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Class_1(object):

    __metaclass__ = Meta_1

    def __new__(cls):
        print"Class_1.__new__() before creating an instance."
        instance = super(Class_1, cls).__new__(cls)
        print"Class_1.__new__() about to return instance."
        return instance

    def __init__(self):
        print"entering Class_1.__init__() for instance initialization."
        super(Class_1,self).__init__()
        print"exiting Class_1.__init__()."

现在让我们创建一个Class_1的实例

1
2
3
4
5
6
7
instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

注意,上面的代码实际上只记录任务。每个方法都将实际工作委托给其父方法的实现,从而保持默认行为。由于typeMeta_1的父类(type是默认的父元类),并且考虑到上面输出的顺序,我们现在知道了type.__call__()的伪实现是什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class type:
    def __call__(cls, *args, **kwarg):

        # ... maybe a few things done to cls here

        # then we call __new__() on the class to create an instance
        instance = cls.__new__(cls, *args, **kwargs)

        # ... maybe a few things done to the instance here

        # then we initialize the instance with its __init__() method
        instance.__init__(*args, **kwargs)

        # ... maybe a few more things done to instance here

        # then we return it
        return instance

我们可以看到元类的__call__()方法是第一个被调用的方法。然后,它将实例的创建委托给类的__new__()方法,并将初始化委托给实例的__init__()。它也是最终返回实例的那个。

从上面可以看出,元类' __call__()也有机会决定是否最终调用Class_1.__new__()Class_1.__init__()。在执行过程中,它实际上可以返回一个没有被这两种方法所触及的对象。例如,单例模式的这种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Meta_2(type):
    singletons = {}

    def __call__(cls, *args, **kwargs):
        if cls in Meta_2.singletons:
            # we return the only instance and skip a call to __new__()
            # and __init__()
            print ("{} singleton returning from Meta_2.__call__(),"
                  "skipping creation of new instance.".format(cls))
            return Meta_2.singletons[cls]

        # else if the singleton isn't present we proceed as usual
        print"Meta_2.__call__() before creating an instance."
        instance = super(Meta_2, cls).__call__(*args, **kwargs)
        Meta_2.singletons[cls] = instance
        print"Meta_2.__call__() returning new instance."
        return instance

class Class_2(object):

    __metaclass__ = Meta_2

    def __new__(cls, *args, **kwargs):
        print"Class_2.__new__() before creating instance."
        instance = super(Class_2, cls).__new__(cls)
        print"Class_2.__new__() returning instance."
        return instance

    def __init__(self, *args, **kwargs):
        print"entering Class_2.__init__() for initialization."
        super(Class_2, self).__init__()
        print"exiting Class_2.__init__()."

让我们来观察当反复尝试创建类型为Class_2的对象时会发生什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.

b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True

元类是一个类,它告诉应该如何创建(一些)其他类。

在这种情况下,我把元类看作是我的问题的解决方案:我有一个非常复杂的问题,也许可以用不同的方法来解决,但是我选择使用元类来解决它。由于其复杂性,它是我编写的少数几个模块之一,其中模块中的注释超过了已经编写的代码量。这里是……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env python

# Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.

# This requires some explaining.  The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried.  I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to.  See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType.  This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient.  The complicated bit
# comes from requiring the GsyncOptions class to be static.  By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace.  Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet.  The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method.  This is the first and only time the class will actually have its
# dictionary statically populated.  The docopt module is invoked to parse the
# usage document and generate command line options from it.  These are then
# paired with their defaults and what's in sys.argv.  After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored.  This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times.  The __getattr__ call hides this by default, returning the
# last item in a property's list.  However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
    class GsyncListOptions(object):
        __initialised = False

    class GsyncOptionsType(type):
        def __initialiseClass(cls):
            if GsyncListOptions._GsyncListOptions__initialised: return

            from docopt import docopt
            from libgsync.options import doc
            from libgsync import __version__

            options = docopt(
                doc.__doc__ % __version__,
                version = __version__,
                options_first = True
            )

            paths = options.pop('<path>', None)
            setattr(cls,"destination_path", paths.pop() if paths else None)
            setattr(cls,"source_paths", paths)
            setattr(cls,"options", options)

            for k, v in options.iteritems():
                setattr(cls, k, v)

            GsyncListOptions._GsyncListOptions__initialised = True

        def list(cls):
            return GsyncListOptions

        def __getattr__(cls, name):
            cls.__initialiseClass()
            return getattr(GsyncListOptions, name)[-1]

        def __setattr__(cls, name, value):
            # Substitut option names: --an-option-name for an_option_name
            import re
            name = re.sub(r'^__',"", re.sub(r'-',"_", name))
            listvalue = []

            # Ensure value is converted to a list type for GsyncListOptions
            if isinstance(value, list):
                if value:
                    listvalue = [] + value
                else:
                    listvalue = [ None ]
            else:
                listvalue = [ value ]

            type.__setattr__(GsyncListOptions, name, listvalue)

    # Cleanup this module to prevent tinkering.
    import sys
    module = sys.modules[__name__]
    del module.__dict__['GetGsyncOptionsType']

    return GsyncOptionsType

# Our singlton abstract proxy class.
class GsyncOptions(object):
    __metaclass__ = GetGsyncOptionsType()

type实际上是一个metaclass——一个创建另一个类的类。大多数metaclasstype的子类。metaclass接收new类作为其第一个参数,并提供对类对象的访问,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> class MetaClass(type):
...     def __init__(cls, name, bases, attrs):
...         print ('class name: %s' %name )
...         print ('Defining class %s' %cls)
...         print('Bases %s: ' %bases)
...         print('Attributes')
...         for (name, value) in attrs.items():
...             print ('%s :%r' %(name, value))
...

>>> class NewClass(object, metaclass=MetaClass):
...    get_choch='dairy'
...
class name: NewClass
Bases <class 'object'>:
Defining class <class 'NewClass'>
get_choch :'dairy'
__module__ :'builtins'
__qualname__ :'NewClass'

Note:

注意,该类没有在任何时候实例化;创建类的简单操作触发了metaclass的执行。


<

tl;博士版本/ hh2 >

函数获取对象的类型。

类的type()是它的元类。

使用元类:

1
2
class Foo(object):
    __metaclass__ = MyMetaClass

Python类本身就是元类的对象(例如)。

默认元类,当您确定类为:

1
2
class foo:
    ...

元类用于将某些规则应用于整个类集。例如,假设您正在构建一个ORM来访问数据库,和你想要记录每个表的类映射到表(根据字段,业务规则、等),可能使用元类例如,连接池的逻辑,它是由所有类分享所有表的记录。另一个用途是逻辑来支持外键,这涉及到多个记录类。

当您定义元类时,您将子类化类型,并可以覆盖以下神奇的方法来插入逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class somemeta(type):
    __new__(mcs, name, bases, clsdict):
     """
  mcs: is the base metaclass, in this case type.
  name: name of the new class, as provided by the user.
  bases: tuple of base classes
  clsdict: a dictionary containing all methods and attributes defined on class

  you must return a class object by invoking the __new__ constructor on the base metaclass.
 ie:
    return type.__call__(mcs, name, bases, clsdict).

  in the following case:

  class foo(baseclass):
        __metaclass__ = somemeta

  an_attr = 12

  def bar(self):
      ...

  @classmethod
  def foo(cls):
      ...

      arguments would be : ( somemeta,"foo", (baseclass, baseofbase,..., object), {"an_attr":12,"bar": <function>,"foo": <bound class method>}

      you can modify any of these values before passing on to type
     """

      return type.__call__(mcs, name, bases, clsdict)


    def __init__(self, name, bases, clsdict):
     """
      called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
     """

      pass


    def __prepare__():
       """
        returns a dict or something that can be used as a namespace.
        the type will then attach methods and attributes from class definition to it.

        call order :

        somemeta.__new__ ->  type.__new__ -> type.__init__ -> somemeta.__init__
       """

        return dict()

    def mymethod(cls):
       """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
       """

        pass

无论如何,这两个是最常用的钩子。元压缩功能非常强大,上面的元压缩功能还远远不够,而且还没有详尽的使用清单。


函数的作用是:返回一个对象的类型或创建一个新类型,

例如,我们可以用type()函数创建一个Hi类,而不需要对class Hi(object)使用这种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
def func(self, name='mike'):
    print('Hi, %s.' % name)

Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.

type(Hi)
type

type(h)
__main__.Hi

除了使用type()动态创建类外,还可以控制类的创建行为并使用元类。

根据Python对象模型,类就是对象,因此类必须是另一个特定类的实例。默认情况下,Python类是类型类的实例。也就是说,类型是大多数内置类的元类和用户定义类的元类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class CustomList(list, metaclass=ListMetaclass):
    pass

lst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')

lst
['custom_list_1', 'custom_list_2']

当我们在元类中传递关键字参数时,Magic将生效,它指示Python解释器通过ListMetaclass创建CustomList。new(),此时,我们可以修改类定义,例如,添加一个新方法,然后返回修改后的定义。


除了已发布的答案之外,我可以说metaclass定义了类的行为。因此,您可以显式地设置元类。每当Python获得一个关键字class,它就开始搜索metaclass。如果没有找到,则使用默认元类类型创建类的对象。使用__metaclass__属性,您可以设置类的metaclass:

1
2
3
4
5
6
class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

它会产生这样的输出:

1
class 'type'

当然,您可以创建自己的metaclass来定义使用您的类创建的任何类的行为。

为此,必须继承默认的metaclass类型类,因为这是主metaclass:

1
2
3
4
5
6
7
8
9
10
class MyMetaClass(type):
   __metaclass__ = type
   # you can write here any behaviour you want

class MyTestClass:
   __metaclass__ = MyMetaClass

Obj = MyTestClass()
print(Obj.__metaclass__)
print(MyMetaClass.__metaclass__)

输出将为:

1
2
class '__main__.MyMetaClass'
class 'type'