如何理解Python中的元类metaclass
以下理解整理翻译自Stackoverflow-What are metaclasses in Python?
关于这个已经有博主翻译转载过了,深刻理解Python中的元类(metaclass)
1、Python中的类
在理解元类之前,你需要掌握Python中的类。Python中借用了SmallTalk语言特性对类进行实现。
在大部分的语言中,类一般是一段生成对象的代码,这在Python中也一样。
>>> class ObjectCreator(object): ... pass ... >>> my_object = ObjectCreator() >>> print(my_object) <__main__.ObjectCreator object at 0x8974f2c>
在Python中,类也是一种对象。当执行如下代码时,Python会申请一块内存保存名为ObjectCreator的对象。
>>> 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>
2.动态生成类
类可以像对象一样动态生成。
首先使用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>
以上方式并不是那么动态,因为你需要自己定义整个类。而类本身作为一个对象是可以通过其他来生成的,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会根据输入参数的不同而表现出两种完全不同的功能,主要是需要满足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 %可以换成另一种方式: >>> 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作为类的名字,同时也用同样的名字对该类进行引用。这两个名字可以不一样,如a=type('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
当你需要给你的类添加方法(属性)的时候,通过函数段定义,并将其传递给type输入属性参数即可。
>>> 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
这样你就可以为创建的类动态的添加你需要的属性。
3、那么什么是元类呢?
元类(Metaclass)是创建类的元素。之前已经了解了可以通过类来创建实例对象,那么元类可以用来创建类对象,是类的类。
MyClass = MetaClass() my_object = MyClass()
结合上文通过type来创建MyClass,我们可以了解type实际上就是元类,Python中使用type作为所有类的元类。可以通过__class__属性来查看类别。在Python中一切都是对象,不管是整数、字符串、函数还是类。但追根溯源,type是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'> >>> age.__class__.__class__ <type 'type'> >>> name.__class__.__class__ <type 'type'> >>> foo.__class__.__class__ <type 'type'> >>> b.__class__.__class__ <type 'type'>
type是內建元类(built-in),但是可以创建自己的元类。
__metaclass__属性
在Python2中,你可以在创建类时给类添加一个__metaclass__属性,Python会使用这个元类属性来创建类对象Foo
class Foo(object): __metaclass__ = something...
当执行class Foo(object)时,Python首先在__metaclass__中寻找类的定义,如果有,就用来创建类对象Foo,如果没有找到,Python会调用type方法创建类对象Foo。
当执行以下代码时,Python的编译过程如下:
class Foo(Bar): pass
判断Foo中是否有__metaclass__属性?如果有,分配内存并使用__metaclass__属性创建以Foo为名的类对象;如果没有找到,Python会在当前模块里寻找该属性;如果还没有找到,那就会用父类Bar的元类(可能是默认的type)来创建类对象。这里需要注意,Foo从Bar处继承,但不会继承Bar的__metaclass__属性。如果Bar的使用type()来作为__metaclass__机制创建子类的话,那么子类不会继承这种创建机制。
而在Python3.x中,元类语法与2.x中不同。
class Foo(object, metaclass=something): ...
__metaclass__属性换成了关键字参数,传递给类对象,但元类的其他行为大体是相同的。3.x中新增的是可以按照关键字参数将属性传递给元类,如下:
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2): ...
4、自定义元类
元类的主要作用是,在创建类时自动改变类的一些属性。当进行类似于调用APIs操作时需要创建对应匹配的类。
举一个例子,如果你想把模块中的所有类的属性名都使用大写,那其中一个可以实现的方式就是使用元类定义,设置__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'
现在我们换成另一个方式,定义一个元类:
# 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__方法总是把它定义的类作为第一个参数接受,类似于普通类定义中的self,为简洁可用cls来代替。通常可以使用较短的参数名,如下:
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)
回到Python3.x中对元类的使用:
class Foo(object, metaclass=Thing, kwarg1=value1): ... %可以转换成如下: class Thing(type): def __new__(class, clsname, bases, dct, kwargs1=default): ...
元类的介绍差不多就这些。
元类在实际中最主要的应用是生成API,典型应用是Django的ORM
翻译水平有限,如有错误,烦请指出。
补充:
浅谈 Python 中的 __init__ 和 __new__
[Python] Python 之 __new__() 方法与实例化
python3中类的重点与难点:__new__方法与__init__方法
python 中 super函数的使用
Python: super 没那么简单