什么是Python中的元类?

什么是元类? 你用什么来做?

元类是一个类的类。 就像一个类定义一个类的实例的行为一样,元类定义了一个类的行为。 一个类是一个元类的一个实例。

元类图

而在Python中,可以使用任意的可调用元素(比如Jerub显示),更有用的方法实际上是使其成为实际的类本身。 type是Python中通常的元类。 如果你想知道,是的, type本身就是一个类,它是它自己的types。 你将无法重新创build类似于Python的type ,但是Python会作弊。 要在Python中创build自己的元类,您只需要子type

元类最常用作类工厂。 就像通过调用类创build类的实例一样,Python通过调用元类来创build一个新的类(当它执行'class'语句时)。 结合正常的__init____new__方法,元类因此允许你在创build一个类的时候做“额外的事情”,比如用一些registry来注册新类,或者甚至完全用别的东西来replace类。

class语句被执行时,Python首先作为一个普通的代码块执行class语句的主体。 由此产生的命名空间(一个字典)保存待分类的属性。 元类是通过查看将被类(元类是inheritance的)的基类,在将被类(如果有的话)或__metaclass__全局variables的__metaclass__属性处来确定的。 元类然后调用类的名称,基类和属性来实例化它。

然而,元类实际上定义了一个类的types ,而不仅仅是一个工厂,所以你可以做更多的事情。 例如,您可以在元类上定义常规方法。 这些元类方法就像类方法,因为它们可以在没有实例的情况下在类上调用,但是它们也不像类方法,因为它们不能在类的实例上调用。 type.__subclasses__()type元类上方法的一个例子。 您还可以定义常规的“魔术”方法,如__add__ __iter____getattr__ __add____getattr__ ,以实现或更改类的行为方式。

以下是一些小部分的汇总示例:

 def make_hook(f): """Decorator to turn 'foo' method into '__foo__'""" f.is_hook = 1 return f class MyType(type): def __new__(cls, 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, cls).__new__(cls, 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__ 

类作为对象

在理解元类之前,你需要掌握Python中的类。 而且Python从Smalltalk语言中借用了一个非常奇怪的概念。

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

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

但是类不仅仅是Python。 类也是对象。

是的,对象。

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

 >>> class ObjectCreator(object): ... pass ... 

在内存中创build名为“ObjectCreator”的对象。

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

但是,这仍然是一个客体,因此:

  • 你可以把它分配给一个variables
  • 你可以复制它
  • 你可以添加属性
  • 您可以将其作为函数parameter passing

例如:

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

dynamic创build类

由于类是对象,因此可以像任何对象一样快速创build它们。

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

 >>> 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。

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

当你使用class关键字时,Python会自动创build这个对象。 但是与Python中的大多数事情一样,它给了你一个手动的方法。

记得functiontype ? 良好的旧function,让你知道什么types的对象是:

 >>> print(type(1)) <type 'int'> >>> print(type("1")) <type 'str'> >>> print(type(ObjectCreator)) <type 'type'> >>> print(type(ObjectCreator())) <class '__main__.ObjectCreator'> 

那么, type有一个完全不同的能力,它也可以在飞行中创build类。 type可以把一个类的描述作为参数,并返回一个类。

(我知道,根据你传递给它的参数,相同的函数可能有两种完全不同的用法,这很愚蠢,这是由于Python中的向后兼容性问题)

type这样工作:

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

例如:

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

可以这样手动创build:

 >>> 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”作为类的名字,并作为variables来保存类的引用。 他们可以是不同的,但没有理由使事情复杂化。

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

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

可以翻译成:

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

并作为一个普通的类使用:

 >>> print(Foo) <class '__main__.Foo'> >>> print(Foo.bar) True >>> f = Foo() >>> print(f) <__main__.Foo object at 0x8a9b84c> >>> print(f.bar) True 

当然,你可以inheritance它,所以:

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

将会:

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

最终你会想要添加方法到你的class级。 只需定义一个具有适当签名的函数,并将其分配为一个属性即可。

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

在dynamic创build类之后,您可以添加更多的方法,就像向正常创build的类对象中添加方法一样。

 >>> def echo_bar_more(self): ... print('yet another method') ... >>> FooChild.echo_bar_more = echo_bar_more >>> hasattr(FooChild, 'echo_bar_more') True 

您将看到我们要去的地方:在Python中,类是对象,您可以dynamic地创build一个类。

这就是Python在使用关键字class时所做的事情,而且它是通过使用元类来实现的。

什么是元类(最后)

元类是创build类的“东西”。

你定义类来创build对象,对吧?

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

那么,元类是创build这些对象。 他们是class级,你可以这样描绘他们:

 MyClass = MetaClass() MyObject = MyClass() 

你已经看到这种type可以让你做这样的事情:

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

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

现在你想知道为什么它是用小写字母写的,而不是Type

那么,我想这是一个与str一致的问题,创buildstring对象的类, int创build整数对象的类。 type只是创build类对象的类。

你可以通过检查__class__属性来看到。

一切,我的意思是一切,都是Python中的一个对象。 包括整数,string,函数和类。 所有这些都是对象。 所有这些都是从一个class级创build的:

 >>> 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__

 >>> age.__class__.__class__ <type 'type'> >>> name.__class__.__class__ <type 'type'> >>> foo.__class__.__class__ <type 'type'> >>> b.__class__.__class__ <type 'type'> 

所以,元类就是创build类对象的东西。

如果你愿意,你可以称之为“class级工厂”。

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

__metaclass__属性

编写类时,可以添加__metaclass__属性:

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

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

小心,这很棘手。

你先写class Foo(object) ,但是类对象Foo不是在内存中创build的。

Python将在类定义中查找__metaclass__ 。 如果find它,它将使用它来创build对象类Foo 。 如果没有,它将使用type来创build类。

多读几遍。

当你这样做时:

 class Foo(Bar): pass 

Python执行以下操作:

Foo是否有__metaclass__属性?

如果是的话,在内存中创build一个类对象(我说的是一个类对象,留在这里),用名字Foo来使用__metaclass__

如果Python找不到__metaclass__ ,它将在MODULE级别寻找一个__metaclass__ ,并试图做同样的事情(但是只对不inheritance任何东西的类,基本上是旧式的类)。

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

在这里要小心__metaclass__属性不会被inheritance,父类( Bar.__class__ )的元类将会被inheritance。 如果Bar使用了一个__metaclass__属性,它创build了带有type() (而不是type.__new__() )的Bar ,那么这个子类将不会inheritance这个行为。

现在最大的问题是,你可以在__metaclass____metaclass__什么?

答案是:可以创build一个类的东西。

什么可以创build一个类? type ,或任何子类或使用它。

自定义元类

元类的主要目的是在创build时自动更改类。

您通常对API进行此操作,您需要创build与当前上下文相匹配的类。

想象一个愚蠢的例子,你决定你的模块中的所有类都应该用大写字母来写属性。 有几种方法可以做到这一点,但一种方法是在模块级别设置__metaclass__

这样,这个模块的所有类都将使用这个元类来创build,我们只需要告诉元类将所有的属性都改为大写。

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

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

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

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

 # 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__ 。 我们开始做吧:

 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 ,所有的论点都有传统的名称。 所以一个真正的生产元类将如下所示:

 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来使它更加清洁,这将减轻inheritance(因为是的,你可以有元类,inheritance于类inheritance的元类):

 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) 

而已。 关于元类没有什么更多。

使用元类代码的复杂性背后的原因不是因为元类,这是因为你通常使用元类来依靠内省来做扭曲的东西,操纵inheritance,__ __dict__等variables。

事实上,元类对于做黑魔法特别有用,因此也是复杂的东西。 但是它们本身就很简单:

  • 拦截一个类的创build
  • 修改类
  • 返回修改的类

为什么你会使用元类而不是函数?

由于__metaclass__可以接受任何可调用,为什么你会使用一个类,因为它显然更复杂?

这样做有几个原因:

  • 意图很清楚。 当你阅读UpperAttrMetaclass(type) ,你会知道接下来会发生什么
  • 你可以使用OOP。 元类可以从元类inheritance,重写父类的方法。 元类甚至可以使用元类。
  • 如果你指定了一个元类,而不是一个元类函数,那么类的子类将是它的元类的实例。
  • 你可以更好地构build你的代码。 你从来没有像上面的例子那样使用元类来做一些微不足道的事情。 这通常是复杂的。 有能力创build几个方法并将它们分组在一个类中,这对于使代码更易于阅读非常有用。
  • 你可以挂钩__new__ __init____init____call__ 。 这将允许你做不同的东西。 即使通常你可以在__new__ ,有些人使用__init__更加舒适。
  • 这些被称为元类,该死的! 这意味着什么!

你为什么要使用元类?

现在是个大问题。 为什么你会使用一些模糊的错误倾向function?

那么,通常你不会:

元类是更深的魔法,99%的用户不应该担心。 如果你想知道你是否需要他们,你不需要(那些真正需要他们的人肯定知道他们需要他们,不需要为什么解释)。

Python大师蒂姆·彼得斯

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

它允许你定义这样的东西:

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

但是,如果你这样做:

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

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

这是可能的,因为models.Model定义了__metaclass__ ,它使用了一些魔法,将刚刚定义的Person变成一个复杂的数据库字段。

Django通过暴露一个简单的API并使用元类来创build一些复杂的外观,从这个API重新创build代码,在幕后做真正的工作。

最后的话

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

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

 >>> class Foo(object): pass >>> id(Foo) 142630324 

一切都是Python中的一个对象,它们都是类的实例或元类的实例。

除了type

type实际上是它自己的元类。 这不是在纯Python中可以重现的东西,而是通过在实现级别上作弊。

其次,元类是复杂的。 你可能不想用它们来进行非常简单的课堂改动。 你可以通过使用两种不同的技术来改变类:

  • 猴子补丁
  • 类装饰器

99%的时间你需要改变class级,你最好使用这些。

但98%的时间,你根本不需要改变class级。

注意,这个答案适用于Python 2.x,因为它是在2008年编写的,元类在3.x中略有不同,请参阅注释。

元类是“class级”工作的秘诀。 新样式对象的默认元类称为“types”。

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

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

这是秘密的起点。 在这个例子的类定义中找出名字,基地和字典来自哪里。

 class ThisIsTheName(Bases, Are, Here): All_the_code_here def doesIs(create, a): dict 

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

 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' 

而现在,一个实际上意味着什么的例子,这将自动使列表中的variables设置在类“attributes”上,并设置为None。

 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”获得的神奇行为不会传递给Initalised的子类。

下面是一个更具体的例子,展示了如何通过子类“type”来创build一个元类,该类在创build类时执行一个动作。 这非常棘手:

 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模型 ,他们的定义看起来有点混乱。 它看起来好像只是定义类属性:

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

然而,在运行时,Person对象被各种有用的方法所填充。 看到一些惊人的metaclassery 来源 。

其他人已经解释了元类是如何工作的以及它们如何适应Pythontypes系统。 这是一个他们可以使用的例子。 在我写的testing框架中,我想跟踪定义类的顺序,以便稍后可以按此顺序对它们进行实例化。 我发现使用元类来做到这一点最简单。

 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的子类都会得到一个类属性_order ,logging类定义的顺序。

我认为ONLamp对元类编程的介绍已经写得很好,尽pipe已经有好几年了,但对于这个话题还是有很好的介绍。

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html

总之:一个类是创build一个实例的蓝图,一个元类是创build一个类的蓝图。 可以很容易地看出,在Python类中也需要成为第一类对象来启用这种行为。

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

  • 创build一个新的模型
  • 元类启用这个

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

什么是元类? 你用什么来做?

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

伪代码:

 >>> Class(...) instance 

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

 >>> Metaclass(...) Class 

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

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

换个angular度来说吧

  • 一个类是一个实例作为一个元类是一个类。

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

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

    同样,当我们用默认的元类明确地定义一个类时,我们实例化它:

     >>> type('Object', (object,), {}) # instantiation of metaclass <class '__main__.Object'> # instance 
  • 换句话说,一个类是一个元类的一个实例:

     >>> isinstance(object, type) True 
  • 第三种方式,元类是一个类的类。

     >>> type(object) == type True >>> object.__class__ <class 'type'> 

当你编写一个类定义并且Python执行它的时候,它使用一个元类来实例化类对象(这个类将被用来实例化这个类的实例)。

就像我们可以使用类定义来改变自定义对象实例的行为一样,我们可以使用元类的定义来改变类对象的行为方式。

他们可以用来做什么? 从文档 :

元类的潜在用途是无限的。 一些已经探索的想法包括日志logging,接口检查,自动委派,自动属性创build,代理,框架和自动资源locking/同步。

尽pipe如此,通常鼓励用户避免使用元类,除非绝对必要。

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

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

 class Foo(object): 'demo' 

你实例化一个类对象。

 >>> Foo <class '__main__.Foo'> >>> isinstance(Foo, type), isinstance(Foo, object) (True, True) 

它与函数调用具有适当参数的type相同,并将结果赋值给该名称的variables:

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

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

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

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

(关于类__module____module__的内容的一个注意__module__是因为类必须知道它们被定义的位置,而__dict____weakref__在那里,因为我们没有定义__slots__ – 如果我们定义了__slots__我们将保存一些因为我们可以通过排除它们来禁止__dict____weakref__例如:

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

…但我离题了。)

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

以下是类的默认__repr__

 >>> Foo <class '__main__.Foo'> 

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

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

So now when we create an object with this metaclass, the __repr__ echoed on the command line provides a much less ugly sight than the default:

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

With a nice __repr__ defined for the class instance, we have a stronger ability to debug our code. However, much further checking with eval(repr(Class)) is unlikely (as functions would be rather impossible to eval from their default __repr__ 's).

An expected usage: __prepare__ a namespace

If, for example, we want to know in what order a class's methods are created in, we could provide an ordered dict as the namespace of the class. We would do this with __prepare__ which returns the namespace dict for the class if it is implemented in Python 3 :

 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 

和用法:

 class OrderedMethodsObject(object, metaclass=OrderedType): def method1(self): pass def method2(self): pass def method3(self): pass def method4(self): pass 

And now we have a record of the order in which these methods (and other class attributes) were created:

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

Note, this example was adapted from the documentation – the new enum in the standard library does this.

So what we did was instantiate a metaclass by creating a class. We can also treat the metaclass as we would any other class. It has a method resolution order:

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

And it has approximately the correct repr (which we can no longer eval unless we can find a way to represent our functions.):

 >>> 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__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>}) 

Python 3 update

There are (at this point) two key methods in a metaclass:

  • __prepare__ , and
  • __new__

__prepare__ lets you supply a custom mapping (such as an OrderedDict ) to be used as the namespace while the class is being created. You must return an instance of whatever namespace you choose. If you don't implement __prepare__ a normal dict is used.

__new__ is responsible for the actual creation/modification of the final class.

A bare-bones, do-nothing-extra metaclass would like:

 class Meta(type): def __prepare__(metaclass, cls, bases): return dict() def __new__(metacls, cls, bases, clsdict): return super().__new__(metacls, cls, bases, clsdict) 

一个简单的例子:

Say you want some simple validation code to run on your attributes — like it must always be an int or a str . Without a metaclass, your class would look something like:

 class Person: weight = ValidateType('weight', int) age = ValidateType('age', int) name = ValidateType('name', str) 

As you can see, you have to repeat the name of the attribute twice. This makes typos possible along with irritating bugs.

A simple metaclass can address that problem:

 class Person(metaclass=Validator): weight = ValidateType(int) age = ValidateType(int) name = ValidateType(str) 

This is what the metaclass would look like (not using __prepare__ since it is not needed):

 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) 

A sample run of:

 p = Person() p.weight = 9 print(p.weight) p.weight = '9' 

生产:

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

Note : This example is simple enough it could have also been accomplished with a class decorator, but presumably an actual metaclass would be doing much more.

The 'ValidateType' class for reference:

 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 

A metaclass is a class that tells how (some) other class should be created.

This is a case where I saw metaclass as a solution to my problem: I had a really complicated problem, that probably could have been solved differently, but I chose to solve it using a metaclass. Because of the complexity, it is one of the few modules I have written where the comments in the module surpass the amount of code that has been written. 这里是…

 #!/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() 

Role of a metaclass's __call__() method when creating a class instance

If you've done Python programming for more than a few months you'll eventually stumble upon code that looks like this:

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

The latter is possible when you implement the __call__() magic method on the class.

 class SomeClass(object): # ... # some definition here ... # ... def __call__(self, foo, bar): return bar + foo 

The __call__() method is invoked when an instance of a class is used as a callable. But as we've seen from previous answers a class itself is an instance of a metaclass, so when we use the class as a callable (ie when we create an instance of it) we're actually calling its metaclass's __call__() method. At this point most Python programmers are a bit confused because they've been told that when creating an instance like this instance = SomeClass() you're calling it's __init__() method. Some who've dug a bit deeper know that before __init__() there's __new__() . Well, today another layer of truth is being revealed, before __new__() there's the metaclass's __call__() .

Let's study the method call chain from specifically the perspective of creating an instance of a class.

This is a metaclass that logs exactly the moment before an instance is created and the moment it's about to return it.

 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 

This is a class that uses that metaclass

 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__()." 

And now let's create an instance of Class_1

 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. 

The code above doesn't actually do anything other than logging the task and then delegating the actual work to the parent (ie keeping the default behavior). So with type being Meta_1 's parent class, we can imagine that this would be the pseudo implementation of type.__call__() :

 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 

We can see that the metaclass's __call__() method is the one that's called first. It then delegates creation of the instance to the class's __new__() method and initialization to the instance's __init__() . It's also the one that ultimately returns the instance.

From the above it stems that the metaclass's __call__() is also given the opportunity to decide whether or not a call to Class_1.__new__() or Class_1.__init__() will eventually be made. Over the course of its execution it could actually return an object that hasn't been touched by either of these methods. Take for example this approach to the singleton pattern:

 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__()." 

Let's observe what happens when repeatedly trying to create an object of type Class_2

 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 

type is actually a metaclass — a class that creates another classes. Most metaclass are the subclasses of type . The metaclass receives the new class as its first argument and provide access to class object with details as mentioned below:

 >>> 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:

Notice that the class was not instantiated at any time; the simple act of creating the class triggered execution of the metaclass .

The tl;dr version

The type(obj) function gets you the type of an object.

The type() of a class is its metaclass .

To use a metaclass:

 class Foo(object): __metaclass__ = MyMetaClass 

Python classes are themselves objects – as in instance – of their meta-class.

The default metaclass, which is applied when when you determine classes as:

 class foo: ... 

meta class are used to apply some rule to an entire set of classes. For example, suppose you're building an ORM to access a database, and you want records from each table to be of a class mapped to that table (based on fields, business rules, etc..,), a possible use of metaclass is for instance, connection pool logic, which is share by all classes of record from all tables. Another use is logic to to support foreign keys, which involves multiple classes of records.

when you define metaclass, you subclass type, and can overrided the following magic methods to insert your logic.

 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 

anyhow, those two are the most commonly used hooks. metaclassing is powerful, and above is nowhere near and exhaustive list of uses for metaclassing.

A metaclass is essentially an abstract base class–a concept taught in most intermediate computer programming courses.

Interesting Posts