点击上方“中兴开发者社区”,关注咱们python
天天读一篇一线开发者原创好文git
Python中的元类是什么?github
原问题地址:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python数据库
什么是元类?使用它们能作什么?编程
元类是类的一种。正如类定义了实例功能,元类也定义了类的功能。类是元类的实例。ide
在Python中你能够随意调用元类(参考Jerub的回答),实际上更有用的方法是使其自己成为一个真正的类。type是Python中经常使用的元类。你可能以为奇怪,是的,type自己就是一个类,并且它就是type类型。你没法在Python中从新创造出彻底像type这样的东西,但Python提供了一个小把戏。你只须要把type做为子类,就能够在Python中建立本身的元类。函数
元类是最经常使用的一类工厂。就像你经过调用类来建立类实例,Python也是经过调用元类来建立一个新类(当它执行class语句的时候),结合常规的 __init__
和__new__
方法,元类能够容许你在建立类的时候作一些“额外的事情”,like registering the new class with some registry(暂时不知道这句话的含义,不知道怎么翻译,字面意思是:就像用某个注册表来注册新的类那样),甚至能够用别的东西彻底代替已有的类。优化
当Python执行class语句时,它首先把整个的class语句做为一个正常的代码块来执行。由此产生的命名空间(一个字典)具备待定类的属性。元类取决于待定类的基类(元类是具备继承性的)、或待定类的__metaclass__
属性(若是有的话)或__metaclass__
全局变量。接下来,用类的名称、基类和属性调用元类,从而把元类实例化。this
然而,元类实际上定义的一个类的类型,而不仅是类工厂,因此你能够用元类来作更多的事情。例如,你能够定义元类的通常方法。这些元类方法和类方法有类似之处,由于它们能够被没有实例化的类调用。但这些元类方法和类方法也有不一样之处,元类方法不能在类的实例中被调用。type.__subclasses__()
是关于type的一个方法。你也能够定义常规的“魔法”函数,如__add__
, __iter__
和__getattr__
,以便实现或修改类的功能。spa
摘抄一个例子:
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__ shareedit
在理解元类以前,你须要掌握Python中的类。Python对于类的定义很特别,这是从Smalltalk语言中借鉴来的。
在大多数语言中,类只是描述如何建立一个对象的代码段。Python中的类大致上也是如此:
>>> class ObjectCreator(object): ... pass ... >>> my_object = ObjectCreator() >>> print(my_object) <__main__.ObjectCreator object at 0x8974f2c>
而Python中的类并非仅限于此。它的类也是对象。
是的,对象。
当你使用关键字class时,Python执行它并建立一个对象。下面是有关的指令
>>> class ObjectCreator(object): ... pass ...
这个对象(类)自己就可以建立一些对象(实例),这就是为何它是类。
但它仍然是一个对象,于是:
你能够将它分配给一个变量
你能够复制它
你能够增长它的属性
你能够把它做为一个功能参数来用
例如:
>>> 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> Creating classes dynamically
既然类是对象,你就能动态地建立它们,就像建立任何对象那样。
首先,你能够在一个使用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时,Python会自动建立该对象。可是,与Python中的大多数东西同样,它给你提供了一个手工操做的方法。
还记得type函数吗?这个一个好用的旧函数,它能让你了解一个对象的类型:
>>> 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是这样应用的:
例如:
>>> class MyShinyClass(object): ... pass
能够这样子来进行手动建立
>>> 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接受字典对于类属性的定义。因此:
>>> 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
固然,你也能够继承它,即:
>>> class FooChild(Foo): ... pass
能够转化为:
>>> FooChild = type('FooChild', (Foo,), {}) >>> print(FooChild) <class '__main__.FooChild'> >>> print(FooChild.bar) # bar is inherited from Foo True
最终,你会想把一些方法添加到你的类中。须要用一个适当的识别标志来定义一个函数,并将其指定为一个属性。
>>> 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
你如今明白了:在Python中,类就是对象,你能够动态地建立类。
这就是关键字class在Python中的应用,它是经过使用元类来发挥做用。
元类是用来建立类的东西。
你经过定义类来建立对象,对吧?
但咱们知道Python的类就是对象。
元类用于建立这些对象,元类是类的类,你能够这样来描述它们:
MyClass = MetaClass() MyObject = MyClass()
你已经看到了type能够这样来用:
MyClass = type('MyClass', (), {})
这是由于type其实是一个元类,做为元类的type在Python中被用于在后台建立全部的类。
如今你感到疑惑的是为何这里是小写的type,而不是大写的Type?
我想这是为了与建立字符串对象的类str和建立整数对象的类int在写法上保持一致。type只是用于建立类的类。
你能够经过检查__class__
的属性来看清楚。
Python中的一切,个人意思是全部的东西,都是对象。包括整数、字符串、函数和类。全部这些都是对象。全部这些都是由一个类建立的:
>>> 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'>
因此,元类只是用于建立类对象的东西。
若是你愿意,你能够把它称为“类工厂”。
type
是Python中内建元类,固然,你也能够建立你本身的元类。
__metaclass__
的属性
当你建立类的时候,能够添加一个__metaclass__
属性:
class Foo(object): __metaclass__ = something... [...]
若是你这样作,Python会使用元类来建立Foo这个类。
当心,这是棘手的。
这是你是首次建立class Foo(object)
,可是类对象Foo在内存中尚未被建立。
Python会在类定义中寻找__metaclass__
。若是找到它,Python会用它来建立对象类Foo。若是没有找到它,Python将使用type来建立这个类。
把上面的话读几遍。
当你写下:
class Foo(Bar): pass
Python会实现如下功能:
Foo有没有__metaclass__
的属性?
若是有,经过借鉴__metaclass__
,用Foo这个名字在内存中建立一个类对象(我说的是一个类对象,记住个人话)。
若是Python找不到__metaclass__
,它会在模块层级寻找__metaclass__
,并尝试作一样的事情(但这只适用于不继承任何东西的类,基本上是旧式类)。
若是它根本找不到任何__metaclass__
,它将使用Bar(第一个父类)本身的元类(这多是默认的type)来建立类对象。
当心点,__metaclass__
属性不会被继承,而父类的元类(Bar.__class__
)将会被继承。若是Bar
所用的__metaclass__
属性是用type()
来建立Bar(而不是type.__new__()
),它的子类不会继承这种功能。
如今最大的问题是,你能够在__metaclass__
中写些什么?
答案是:能够建立类的东西。
什么能够建立类?type,或者父类。
一个元类的主要目的是当它被建立时,这个类能够自动改变。
一般在API中,能够建立一个元类,以之匹配于当前的内容。
想象一个愚蠢的例子:模块中的全部类的属性都应该用大写字母来写。你有几种方法,其中的一种方法就是在模块层次上设置__metaclass__
。
这样,这个模块中全部的类都将使用这个元类来建立,咱们只须要告诉元类把全部属性改成大写。
幸运的是,__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)
但这不是真正的面向对象编程。咱们直接调用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__
以upperattr_metaclass
为第一参数,而且在upperattr_metaclass
中被定义。就像你把self做为实例的第一个参数那样,或者做为类方法的第一个参数。
固然,我在这里为了清晰起见,使用了一个很长的名称,但就像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使它更清楚,这样将缓解继承(由于,是的,你能够拥有元类,继承metaclasses
,继承type):
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)
就是这样。关于元类真的是没有更多要讲的了。
使用元类的代码复杂的缘由并不在于元类,那是由于你一般用元类来实现一些奇怪的功能,而这些功能要依靠自省、继承、变量,如:__dict__
等。
的确,元类对于“魔法”特别有用,这些事务是复杂的,但元类自己很简单:
拦截类的建立
修改类
返回修改后的类
__metaclass__
能够被任意调用。既然类明显更复杂,你为何还要使用它呢?
这样作有几个理由:
目的明确。当你读UpperAttrMetaclass(type)
时,你知道要遵循的是什么。
可使用面向对象编程。元类能够继承元类、重写父类的方法。元类甚至可使用元类。
能够优化代码。你从不为像上面例子中琐碎的东西而使用元类。它一般用于复杂的东西。在一个类中有多种方法而且将它们优化组合,是很是有价值的,使得代码可读性更强。
你可能喜欢使用__new__
,__init__
和__call__
。它们能帮你实现不一样的功能。尽管你一般能够用__new__
来实现全部的功能,有些人仍是更喜欢使用__init__
。
这些被称为元类。可恶!它确定意味着什么!
你为何会使用元类?
如今的大问题是:为何你会使用一些复杂难懂、容易出错的特性?
嗯,一般你不会这样作:
元类是更深层次的魔法,超过99%的用户不须要担忧。不要怀疑你是否须要元类(那些真正须要元类的人对此肯定无疑,而且不须要解释为何)。
——Python专家蒂姆﹒彼得斯
元类的主要使用案例是建立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转化成一个复杂的钩子链接到数据库字段。
经过显示一个简单的API和元类,Django使复杂的东西看起来简单,再从API重构代码去完成真正的幕后工做。
首先,你知道,类是能够建立实例的对象。
事实上,类自己就是实例。在元类中
>>> class Foo(object): pass >>> id(Foo) 142630324
在Python中,一切都是对象,它们都是类的实例或元类的实例。
可是type除外。
type其实是本身的元类。你没法在纯粹的Python中复制它,因此只能在实施层面作点小把戏。
其次,元类是复杂的。你可能不想把它们用于很是简单的类。你能够用两种不一样的技术来改变类:
猴子补丁monkey patching
类装饰器
当你须要改变类的时候,99%的状况下,使用它们是明智之举。
但99%的时间,你根本不须要改变类。
转自:https://github.com/qiwsir/StackOverFlowCn/blob/master/302.md