元类是什么?我们使用它们做什么?
类对象
在理解元类之前,您需要掌握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中,类不仅仅是这样。类也是对象。
是的,对象。
只要使用关键字
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> |
动态创建类
因为类是对象,所以您可以像创建任何对象一样动态地创建它们。
首先,您可以使用
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> |
但它不是动态的,因为你仍然需要自己编写整个类。
由于类是对象,它们必须由某些东西生成。
当您使用
还记得函数
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'> |
嗯,
(我知道,根据传递给它的参数,同一个函数可以有两种完全不同的用途,这很愚蠢。这是一个向后的问题兼容在Python中)
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"作为类的名称并作为保存类引用的变量。它们可以是不同的,但没有理由把事情复杂化。
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在使用关键字
元类是创建类的"东西"。
定义类是为了创建对象,对吧?
但是我们知道Python类是对象。
元类是创建这些对象的。他们是班级的班级,你可以这样想象:
1 2 | MyClass = MetaClass() my_object = MyClass() |
您已经看到
1 | MyClass = type('MyClass', (), {}) |
这是因为函数
现在您想知道为什么它是小写的,而不是
所有东西,我是说所有东西,都是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'> |
现在,任何一个
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'> |
元类就是创建类对象的东西。
如果你愿意,你可以称它为"类工厂"。
__metaclass__ 属性
在python2中,您可以在编写类时添加
1 2 3 | class Foo(object): __metaclass__ = something... [...] |
如果这样做,Python将使用元类创建类
小心,这是棘手。
首先编写
Python将在类定义中查找
读几遍。
当你做的事:
1 2 | class Foo(Bar): pass |
Python执行以下操作:
在
如果是,在内存中创建一个类对象(我说的是类对象,请留在这里),并使用
如果Python找不到
然后,如果它根本找不到任何
这里要注意,
现在最大的问题是,你能在
答案是:可以创建类的东西。
什么可以创建类?
在Python 3中,设置元类的语法已经更改:
1 2 | class Foo(object, metaclass=something): ... |
也就是说,不再使用
然而元类的行为在很大程度上保持不变。
python 3中向元类添加的一件事是,您还可以将属性作为关键字参数传递到元类中,如下所示:
1 2 | class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2): ... |
阅读下面的部分了解python如何处理这个问题。
自定义元类
元类的主要目的是自动改变类,当它创建。
您通常对api这样做,在api中,您希望创建与当前上下文。
想象一个愚蠢的例子,你决定所有的类在你的模块它们的属性应该是大写的。有几种方法这样做,但是一种方法是在模块级设置
这样,这个模块的所有类都将使用这个元类创建,我们只需要告诉元类把所有属性都变成大写。
幸运的是,
我们从一个简单的例子开始,用一个函数。
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。我们直接调用
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) |
您可能已经注意到了额外的参数
当然,为了清楚起见,我在这里使用的名称很长,但是就像对于
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) |
我们可以使用
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(例如
的确,元类对黑魔法特别有用,因此复杂的东西。但就其本身而言,它们很简单:
拦截类创建修改的类返回修改后的类为什么要使用元类而不是函数?既然
这样做有几个原因:
其意图是明确的。当你阅读现在有个大问题。为什么要使用一些容易出错的模糊特性?
通常你不会:
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) |
它不会返回
这是可能的,因为
Django通过公开一个简单的API使复杂的东西看起来很简单使用元类,从这个API重新创建代码来完成真正的工作在幕后。
最后一句话
首先,您知道类是可以创建实例的对象。
实际上,类本身就是实例。元类。
1 2 3 | >>> class Foo(object): pass >>> id(Foo) 142630324 |
在Python中,所有对象都是对象,它们都是类的实例或者元类的实例。
除了
其次,元类是复杂的。您可能不想使用它们非常简单的类更改。您可以使用两种不同的技术来更改类:
猴子打补丁类的修饰符99%的时候你需要修改类,你最好使用这些。
但是98%的时候,你根本不需要修改类。
元类是类的类。类定义类的实例(即对象)的行为方式,而元类定义类的行为方式。类是元类的实例。
虽然在Python中可以为元类使用任意的可调用项(如Jerub所示),但更好的方法是让它本身成为一个实际的类。
元类通常用作类工厂。当您通过调用类来创建对象时,Python通过调用元类来创建一个新类(当它执行"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 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 |
注意,通过使用元类
下面是一个更具体的例子,展示了如何子类化"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 |
任何属于
我认为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 |
上面这些看起来应该很熟悉。那么,
1 2 | >>> Metaclass(...) Class |
在实际代码中,我们可以传递默认的元类,
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 |
同样,当我们显式地用默认元类
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) |
它与函数调用
1 2 3 4 | name = 'Foo' bases = (object,) namespace = {'__doc__': 'demo'} Foo = type(name, bases, namespace) |
注意,有些东西会自动添加到
1 2 3 4 | >>> Foo.__dict__ dict_proxy({'__dict__': , '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': 'demo'}) |
在这两种情况下,我们创建的对象的元类都是
(附注的内容类
1 2 3 | >>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()}) >>> Baz.__dict__ mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'}) |
…但我离题了。)
我们可以像任何其他类定义一样扩展下面是类的默认
1 2 | >>> Foo <class '__main__.Foo'> |
默认情况下,我们在编写Python对象时可以做的最有价值的事情之一就是为它提供一个良好的
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__) |
所以现在当我们用这个元类创建一个对象时,命令行上的
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}) |
通过为类实例定义一个漂亮的
例如,如果我们想知道类的方法是以什么顺序创建的,我们可以提供一个有序的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'>) |
它大约有正确的
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更新
元类中(此时)有两个关键方法:
一个简单的、什么都不做的额外元类想要:
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) |
一个简单的例子:
假设您希望在属性上运行一些简单的验证代码——比如它必须总是
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) |
这就是元类的样子(不使用
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 |
元类'
如果你已经做了几个月的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') |
当您在类上实现
1 2 3 4 5 6 7 | class SomeClass(object): # ... # some definition here ... # ... def __call__(self, foo, bar): return bar + foo |
当类的实例用作可调用的时调用
让我们从创建类实例的角度来研究方法调用链。
这是一个元类,它准确地记录了创建实例之前的时刻以及它将要返回实例的时刻。
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__()." |
现在让我们创建一个
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. |
注意,上面的代码实际上只记录任务。每个方法都将实际工作委托给其父方法的实现,从而保持默认行为。由于
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 |
我们可以看到元类的
从上面可以看出,元类'
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__()." |
让我们来观察当反复尝试创建类型为
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() |
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' |
注意,该类没有在任何时候实例化;创建类的简单操作触发了
<
tl;博士版本/ hh2 >
函数获取对象的类型。
类的
使用元类:
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(),此时,我们可以修改类定义,例如,添加一个新方法,然后返回修改后的定义。
除了已发布的答案之外,我可以说
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' |
当然,您可以创建自己的
为此,必须继承默认的
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' |