元编程,一个听起来特别酷的词,强大的Lisp在这方面是好手,对于Python,尽管没有完善的元编程范式,一些天才的开发者还是创作了很多元编程的魔法。Django的ORM就是元编程的一个很好的例子。
本篇的概念和例子皆在Python3.6环境下
一切都是对象
Python里一切都是对象(object
),基本数据类型,如数字,字串,函数都是对象。对象可以由类(class
)进行创建。既然一切都是对象,那么类是对象吗?
是的,类也是对象,那么又是谁创造了类呢?答案也很简单,也是类,一个能创作类的类,就像上帝一样,开启了万物之始。这样的类,称之为元类(classmeta
)。
类的定义
对象是通过类创建的,这个很好理解。例如下面的代码:
1
2
3
4
5
6
|
class Bar( object ): pass bar = Bar() print (bar, bar.__class__) # <__main__.Bar object at 0x101eb4630> <class '__main__.Bar'> print (Bar, Bar.__class__) # <class '__main__.Bar'> <class 'type'> |
可以看见对象 bar 是类 Bar 创建的实例。然而 Bar,看起来却是由一个叫 type 的类创建的实例。即 bar <-- Bar < -- type
。
上面的例子,对象是动态创建的,类则是通过关键字 class 声明定义的。class关键字背后的玄机是什么呢?
实际上,class Bar(object)
这样的代码,等价于 Bar = type('Bar', (objects, ), {})
即类 type 通过实例化创建了它的对象 Bar,而这个 Bar 恰恰是一个类。这样能创建类的类,就是 Python 的元类。
从创建 Bar 的代码上来看,元类 type 的 __init__
方法有3个参数,
- 第一个是创建的类的名字
- 第二个是其继承父类的元类列表,
- 最后就是一个属性字典,即该类所具有的属性。
type 元类
type是小写,因而很容易误以为它是一个函数。通过help(type)可以看到它的定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class type ( object ): """ type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type """ def __init__( cls , what, bases = None , dict = None ): # known special case of type.__init__ """ type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type # (copied from class doc) """ pass @staticmethod # known case of __new__ def __new__( * args, * * kwargs): # real signature unknown """ Create and return a new object. See help(type) for accurate signature. """ pass |
如前所述,__init__
方法接受三个参数,type 实例化的过程,会创建一个新的类。创建类的代码来自 __new__
方法,它的参数其实和 __init__
,一样。至于它们之间有什么关系,后面再做介绍。目前只要知道,当调用 type 进行实例化的时候,会先自动调用 __new__
方法,然后再接着调用 __init__
方法,在类外面来看,最终会实例化一个对象,这个对象是一个类。
从 type 的定义来看,它继承 object,Python3的所有类,都继承来着 object,类type 也是 object 的实例,令人奇怪的是,object 既是类也是对象,它也是由 type实例化而来。有一种鸡生蛋,蛋生鸡的悖论。暂且先不管,只要知道所有类的顶级继承来自 object 就好。
自定义元类
既然元类可以创建类,那么自定义元类就很简单了,直接继承类 type 即可。先看下面一个例子:
1
2
3
4
5
6
7
8
9
10
|
class MyType( type ): pass pass print (MyType, MyType.__class__) # <class '__main__.MyType'> <class 'type'> print (Bar, Bar.__class__) # <class '__main__.Bar'> <class '__main__.MyType'> |
可以看到,Bar在声明的时候,指定了其元类,此时的类 Bar 的__class__
属性不再是 type,而是 MyType。即之前定义 Bar 的代码不再是 Bar = type('Bar', (objects, ), {})
, 而是 Bar = MyType('Bar', (objects, ), {})
。创建的元类的代码是MyType = type('MyType', (objects, ), {})
。
如果一个类没有显示的指定其元类,那么会沿着继承链寻找父类的元类,如果一直找不到,那么就使用默认的 type 元类。
元类冲突
每个类都可以指定元类,但是父类和子类的元类要是一条继承关系上的,否则会出现元类冲突。并且这个继承关系中,以继承最后面的元类为其元类。
元类的查找顺序大致为,先查看其继承的父类,找到父类的元类即停止。若直接父类没有元类,直到顶级父类 object ,此时父类(object)的元类是 type(basemetaclass),再看其自身有没有指定元类(submetaclass),如果指定了元类(submetaclass),再对比这个子元类(submetaclass)和父元类(basemetaclass),如果它们毫无继承关系,那么将会抛出元类冲突的错误。如果指定的子元类是父元类的父类,那么将会使用父元类,否则将使用期指定的子元类。
即 submetaclass <- basemetaclass
使用 submetaclass
作为最终元类,
若 basemetaclass <- submetaclass
, 使用 basemetaclass
作为最终元类,
两者无继承关系,抛出冲突。
有点像绕口令,且看代码例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class MyType( type ): pass # 等价于 MyType = type('MyType', (object, ), {}) class Bar( object , metaclass = MyType): pass # 等价于 Bar = MyType('Bar', (object, ), {}) class Foo(Bar): pass # 等价于 Foo = MyType('Foo', (Foo, object, ), {}) print (Bar, Bar.__class__) # <class '__main__.Bar'> <class '__main__.MyType'> print (Foo, Foo.__class__) # <class '__main__.Foo'> <class '__main__.MyType'> |
Bar的父元类(basemetaclass)type,指定子元类(submetaclass)是 MyType, MyType 继承自 type,所以Bar的元类是 MyType。
又如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class MyType( type ): pass class Bar( object , metaclass = MyType): pass class Foo(Bar, metaclass = type ): pass print (Bar, Bar.__class__) # <class '__main__.Bar'> <class '__main__.MyType'> print (Foo, Foo.__class__) # <class '__main__.Foo'> <class '__main__.MyType'> |
尽管 Foo 也指定了元类(submetaclass) type,可是其父类的元类(basemetaclass)是 MyType, MyType
是 type的子类,因此 Foo的元类抛弃了指定的(submetaclass) type,而是沿用了其父类的MyType。
当 submetaclass 和 basemetaclass 没有继承关系的时候,将会元类冲突
1
2
3
4
5
6
7
8
9
10
11
12
|
class MyType( type ): pass class MyOtherType( type ): pass class Bar( object , metaclass = MyType): pass class Foo(Bar, metaclass = MyOtherType): pass |
运行代码,当定义的时候就会出现TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict)
元类冲突的错误。
修改代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class MyType( type ): pass class MyOtherType(MyType): pass class Bar( object , metaclass = MyType): pass class Foo(Bar, metaclass = MyOtherType): pass print (Bar, Bar.__class__) # <class '__main__.Bar'> <class '__main__.MyType'> print (Foo, Foo.__class__) # <class '__main__.Foo'> <class '__main__.MyOtherType'> |
可以看到 Bar 和 Foo 分别有自己的元类,并且都符合继承关系中寻找。再调换一下元类看看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class MyType( type ): pass class MyOtherType(MyType): pass class Bar( object , metaclass = MyOtherType): pass class Foo(Bar, metaclass = MyType): pass print (Bar, Bar.__class__) # <class '__main__.Bar'> <class '__main__.MyOtherType'> print (Foo, Foo.__class__) # <class '__main__.Foo'> <class '__main__.MyOtherType'> |
都使用了Foo还是使用了元子类作为元类。究其原因,其实也很好理解。定义父类的时候,使用了元类MyOtherType 。定义子类的时候,通过继承,找到了创建父类的元类,那么父类就是 MyOtherType 的实例。
如果使用 MyType 做为元类,那么他就是 MyType 的实例,MyType的实例会比MyOtherType具有的属性少,那么在继承链上,它又是 Bar的子类,这样看就是子类比父类还狭窄了,显然不是一个好的关系。即变成了下面的关系
Bar <- MyOtherType
| ↑
| |
↓ |Foo <- MyType
因此当 MyType 是 MyOtherType的父类的时候,即使 Foo 指定了 MyType作为元类,还是会被忽略,使用其父元类MyOtherType。
上面的线的箭头要一直,才能使用各自指定的元类,否则使用箭头指向的那个类作为元类。元类没有继承关系,元类冲突。
对象(类)实例化
目前为止,我们了解了类的定义,即类是如何被元类创建出来的,但是创建的细节尚未涉及。即元类是如何通过实例化创建类的过程。这也是对象创建的过程。
前文介绍了一个对象是通过类创建的,类对象是通过元类创建的。创建类中,会先调用元类的__new__方法,设置其名称,继承关系和属性,返回一个实例。然后再调用实例的__init__方法进行初始化实例对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class MyType( type ): def __init__( self , * args, * * kwargs): print ( 'init ' , id ( self ), args, kwargs) def __new__( cls , * args, * * kwargs): print ( 'new' , id ( cls ), args, kwargs) instance = super (MyType, cls ).__new__( cls , * args, * * kwargs) print ( id (instance)) return instance class Bar( object , metaclass = MyType): pass |
运行代码可以看见输出:
new 4323381304 ('Bar', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'Bar'}) {}
4323382232
init 4323382232 ('Bar', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'Bar'}) {}
注意,上面代码仅关注 Bar
类的创建,即 Bar =MyType('Bar', (object, ), {})
这个定义代码。MyType进行实例化创建 Bar的过程中,会先用 其 __new__
方法,后者调用了父类 type的 __new__
方法,并返回了元类的实例, 同时调用这个实例的__init__
方法,后者对改实例对象进行初始化。这也就是为什么方法名为 __init__
。
通常我们会在 __init__
方法初始化一些实例对象的属性如果 __new__
方法什么也不返回,那么 __init__
方法是不会被调用的。
instance = super(MyType, cls).__new__(cls, *args, **kwargs)
, 有的地方也喜欢写成type.__new__
或者type
,前者是python中如何调用父类方法的问题,后者是直接使用type创建类的过程。比较推荐的写法还是使用 super 调用其父类的方法的方式。
类是元类的对象,普通类创建对象的过程,也是一样。因此,只要重写 __new__方法,还可以实现一个类还可以创建另外一个类的实例的魔法。
移花接木
重写 __new__
方法,让其创建另外一个类的实例。
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
|
class Bar: def __init__( self , name): self .name = name print ( 'Bar init' ) def say( self ): print ( 'say: Bar {}' . format ( self .name)) class Foo( object ): def __init__( self ): print ( 'self {}' . format ( self )) def __new__( cls , * args, * * kwargs): instance = super (Foo, cls ).__new__(Bar, * args, * * kwargs) print ( 'instance {}' . format (instance)) instance.__init__( 'a class' ) return instance def say( self ): print ( 'say: Foo' ) m = Foo() print ( 'm {}' . format (m)) m.say() |
输出
instance <__main__.Bar object at 0x104033240>
Bar init
m <__main__.Bar object at 0x104033240>
say: Bar a class
在类 Foo 中,通过重写 __new__返回了一个 Bar 类的实例对象,然后调用 Bar 实例的 __inti__ 方法初始化,由于返回了 bar 实例,因此 Foo 的实例没有被创建,因此也不会调用它的实例方法 __inti__ 。这样就把 移花(Bar)接木(Foo)上了。
也许有人会觉得这样的诡异魔法有什么用呢?实际上,Tornado框架使用了这样的技术实现了一个叫 Configurable 的工厂类,用于创建不同网络IO下的epoll还是select模型。有兴趣可以参考其实现方式。
元类的应用
讨论了那么多原理的东西,最后肯定是要应用到实际中才有意义。既然类可以被动态的创建,那么很多定义在类的方法,岂不是也可以被动态的创建了呢。这样就省去了很多重复工作,也能实现酷酷的元编程。
元类可以创建单例模式,也可以用来实现 ORM,下面介绍的是Django使用元类实现的查找方式。更经典的model定义网上有很多例子,就不再介绍了。下面介绍一个model通过manger管理器实现查询方法的例子
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
|
import inspect class QuerySet: def get( self , * args, * * kwargs): print ( 'get method' ) return self def filter ( self , * args, * * kwargs): print ( 'filter method' ) return self class BaseManager: def __init__( self ): pass @classmethod def from_queryset( cls , queryset_class, class_name = None ): if class_name is None : class_name = '%sFrom%s' % ( cls .__name__, queryset_class.__name__) class_dict = { '_queryset_class' : queryset_class, } class_dict.update( cls ._get_queryset_methods(queryset_class)) return type (class_name, ( cls ,), class_dict) def get_queryset( self ): return self ._queryset_class() @classmethod def _get_queryset_methods( cls , queryset_class): def create_method(name, method): def manager_method( self , * args, * * kwargs): return getattr ( self .get_queryset(), name)( * args, * * kwargs) manager_method.__name__ = method.__name__ manager_method.__doc__ = method.__doc__ return manager_method new_methods = {} for name, method in inspect.getmembers(queryset_class, predicate = inspect.isfunction): if hasattr ( cls , name): continue queryset_only = getattr (method, 'queryset_only' , None ) if queryset_only or (queryset_only is None and name.startswith( '_' )): continue new_methods[name] = create_method(name, method) return new_methods class Manager(BaseManager.from_queryset(QuerySet)): pass class ModelMetaClass( type ): def __new__( cls , * args, * * kwargs): name, bases, attrs = args attrs[ 'objects' ] = Manager() return super (ModelMetaClass, cls ).__new__( cls , name, bases, attrs) class Model( object , metaclass = ModelMetaClass): pass class User(Model): pass User.objects.get() User.objects. filter () User.objects. filter ().get() |
这样model就用使用期管理器Manger 下的方法了。通过model的元类ModelMetaClass,定义model的时候,就初始化了一个 Manger对象挂载到Model下面,而定义Manger的时候,也通过元类将QuerySet下的查询方法挂载到Manger下了。
总结
Python里一切都是对象,对象都是由类进行创建实例化而来。既然一切是对象,那么类也是对象,而类这种对象又是由一种更高级类创建而来,即所谓的元类。
元类可以创建类,Python默认的元类是 type。通过继承type,可以自定义元类,在自定义元类的时候定义或者重载 __new__
,可以创建该类的实例对象,同时也可以修改类创建对象的行为。类通过 __new__
创建实例对象,然后调用实例对象的 __init__
初始化实例对象。
在使用自定义元类的时候,子类的的元类和父类的元类有关系,前者指定的元类必须和父类的元类是一个继承关系上的,否则会出现元类冲突。子类选取元类的取决于指定的元类和父元类的继承关系,子元类若是父元类的子类,则指定的元类为子元类,否则将会被忽略,使用父元类为其元类。
元类是元编程的一种技术手段,常用于实现工厂模式的策略。通过定义元类动态创建类和展开,可以实现很多设计精妙的应用。ORM 正式其中一种常用的方法。
以上就是详解python metaclass(元类)的详细内容,更多关于python metaclass(元类)的资料请关注服务器之家其它相关文章!
原文链接:https://www.jianshu.com/p/b0f4d9c9afbb