Python中metaclass解释
Classes as objects
首先,在认识metaclass之前,你需要认识下python中的class。python中class的奇怪特性借鉴了smalltalk语言。大多数语言中,classes仅仅是用于描述怎样创建一个对象的代码端。在某种程度上说,python中的class也是这样的。
>>> class ObjectCreator(object): ... pass ... >>> my_object = ObjectCreator() >>> print my_object <__main__.ObjectCreator object at 0x8974f2c>
但是,python中的classes同时还是objects,是的,看的没错,是objects,一旦你使用关键字class,python将执行并且生成一个对象(object),命令
>>> class ObjectCreator(object): ... pass ...
将在内存中创建一个名字为ObjectCreator的对象。
这个对象(类)自己具有创建对象(实例)的能力,这也是为什么被称之为类。
因为它是一个对象,所以它应该具有对象的一些特性:
- 你可以把它assign给一个变量
- 你可以copy它
- 你可以给它添加属性
- 你可以把它作为一个函数的参数
例如:
>>> 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
因为classes是对象,所以你可以想对象一样动态的创建他们。
首先,你可以在一个函数中使用关键字创建它:
>>> 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可以将类的描述作为参数,然后生成一个类。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
可以利用以下方式手动创建:
>>> 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作为class的name, 函数很明显了,只不过是些不同的参数变化,没有理由去复杂化,用原文作者的话说:They can be different,but there is no reason to complicate things。
What are metaclasses (finally)
终于要开是解释metaclasses了,我也等了好久,Metaclasses are the 'stuff' that creates classes. 这个是文章的作者说的,就是说它是个可以创建类的东东,你定义类是为了创建对象是吗?但是在python中我们视类为对象,so,metaclasses就是来创建对象的。它们是类的类,你可以这样想象它们:
MyClass = MetaClass() MyObject = MyClass()
你可以看出type让你做了同样的事情:
MyClass = type('MyClass', (), {})
这是因为函数type事实上是一个metaclass,在python中,type是一个metaclass用于在后台创建类。现在你知道为什么它为什么用的是小写,而不是大写的Type?
well, 你是不是想到了str/int等函数呢,str用于创建字符串对象,int创建整型对象,那type只不过是创建类对象的类而已。你可以通过查看__class__属性看到。所有的一切都是对象,所以的对象都可以通过一个函数创建:
>>> 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的创建者是谁呢?
>>> age.__class__.__class__ <type 'type'> >>> name.__class__.__class__ <type 'type'> >>> foo.__class__.__class__ <type 'type'> >>> b.__class__.__class__ <type 'type'>
这下应该稍微明白了些吧,当然,我们也可以创建我们自己的metaclass。
The __metaclass__
attribute
你可以在创建类的时候添加__metaclass__属性:
class Foo(object): __metaclass__ = something... [...]
python将使用metaclass去创建类Foo,要小心哦,你写了class Foo(object),但是Foo还没有在内存中创建,python会在你的类中寻找__metaclass__如果找到了就用它创建,如果没有找到,它将使用type去创建类。
详细点说吧,当你定义类时:
class Foo(object): pass
python将首先查看在类定义中有没有__metaclass__属性,没有将在parent中找,没有去module级别找,如果还没有找到,最后的杀招type。
好,最后的问题是,我们该在__metaclass__中放些什么东东呢?
Custom metaclasses
一个metaclass的主要目的是在类创建的时候自动的去改变它,读起来有点拗。一般来说,当你写些APIs,而且这些APIs要满足当前的上下文的时候,可以考虑使用metaclass。想象一下,当你的module中的类需要将它们的属性写为小写的时候,我们就可以试试在moudle级别metaclass,这时,module中的所有类都将由metaclass创建,我们所要做的是告诉metaclass将所有的属性转化为小写。幸运的是,我们不一定非要将__metaclass__定义为一个类:
# 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'
现在,我们用一个类来实现metaclass:
# 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,这个没有什么特别:一个方法常常将当前的实例作为首个参数,就像平常的方法中的self。当然,我这里用过长的名字视为了更清晰的解释,就像self一样,所有的参数有惯例性的名字,所以一下是个产品性的metaclass:
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让代码更干净:
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)
就这么多,metaclass就是这么简单。其实,使用metaclass的代码复杂的原因不是因为使用了metaclass,而是因为你常常使用metaclass去做些需要introspection,操作继承/变量比如__dict__等。事实上,metaclass也确实能做些复杂的事。但是,以下是不复杂的:
- 理解一个类的创建
- 改变类
- 返回一个修改的类
Why would you use metaclasses classes instead of functions?
还是坚持写完吧!因为__metaclass__能接受任意的调用,那为什么要选用看起来比较复杂的类呢?有一下几个原因:
- 兴建类的目的比较清晰,参考上面的UpperAttrMetaclass(type)
- 你可以使用OOP。Metaclass能继承自metaclass,覆盖父类方法
- 你可以更好的组织你的代码结构。make code easy to read
- 你可以使用__new__, __init__, __call__
- 都将做了metaclass,总要作点事吧!(这个比较牵强(:-)
Why the hell would you use metaclasses?
python guru写的解释道行太浅看不懂,不翻译了,不过他说了个例子,就是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通过expose 一个简单的API,将许多复杂的事情让我们看起来很简单,而且通过metaclass,在后台利用API重新生成code。
The last word
最后,作者提到了type的exception,还有class alterations:意思就是说我们大部分时间是用不到class alteration的,用到的时候呢,可以使用monkey patching和class decorators来实现,所以留给metaclass的空间很小了。
全剧终!!!
文章的详情清查看:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
偶尔翻看stackoverflow的python帖子,受益匪浅,今天看到metaclass讲的太好了,就试着翻译出来试试,欢迎大家互相交流!