metaclass基础
一般情况下,如果要用类来实现metaclass的话,该类需要继承与 type ,而且通常会重写 type 的 __new__方法老控制创建过程。在metaclass里面定义的方法会成为类的方法,可以直接通过类名来调用。
如何使用metaclass
用类的形式:
类继承于 type, 例如 class Meta(type): pass
将需要使用metaclass来构建的类的__metaclass__属性赋值为 Meta。
metaclass原理
metaclass的原理其实是这样的:当定义好类之后,创建类的时候其实是调用了 type 的 __new__ 方法为这个类分配内存空间。创建好了之后再调用 type 的 __init__ 方法初始化。
__new__方法
__new__(cls, name, bases, attrs)
- cls: 将要创建的类,类似于self,但是self指向的是Instance,而这里cls指向的是class
- name: 类的名字,也就是我们通常用类名__name__获取的
- base: 基类
- attrs: 属性的dict,dict的内容可以是变量(类属性),也可以是函数(类方法)
所以在创建类的过程,我们可以在这个函数里面修改name, bases, attrs的值来自由的达到我们的功能。
metaclass的使用示例
修改/增加/删除类或者实例中的方法或属性
#!/usr/bin/python
#coding :utf-8
def ma(cls):
print 'method a'
def mb(cls):
print 'method b'
method_dict = {
'ma': ma,
'mb': mb,
}
class DynamicMethod(type):
def __new__(cls, name, bases, dct):
if name[:3] == 'Abc':
dct.update(method_dict)
return type.__new__(cls, name, bases, dct)
def __init__(cls, name, bases, dct):
super(DynamicMethod, cls).__init__(name, bases, dct)
class AbcTest(object):
__metaclass__ = DynamicMethod
def mc(self, x):
print x * 3
class NotAbc(object):
__metaclass__ = DynamicMethod
def md(self, x):
print x * 3
def main():
a = AbcTest()
a.mc(3)
a.ma()
print dir(a)
b = NotAbc()
print dir(b)
if __name__ == '__main__':
main()
通过metaclass这个元类,可以在那些指定了__metaclass__属性的类里面,根据类名字,如果是以Abc开头的就给它们加上ma和mb方法。
只要我们能操作__new__方法里面的其中一个参数attrs,就可以动态添加东西了。
批量的对某些方法使用decorator,而不需要每次都在方法的上面加上@decorator_func
比如需要验证登陆或者是否有权限的,只有通过验证通过之后才可以操作,那么如果有很多个操作都需要这样做,我们一般情况下可以手动在每个方法的前头用@login_required类似这样的方法。如果使用了metaclass的使用的话,可以这样做:
#!/usr/bin/python
#coding :utf-8
from types import FunctionType
def login_required(func):
print 'login check logic here'
return func
class LoginDecorator(type):
def __new__(cls, name, bases, dct):
for name, value in dct.iteritems():
if name not in ('__metaclass__', '__init__', '__module__') and\
type(value) == FunctionType:
value = login_required(value)
dct[name] = value
return type.__new__(cls, name, bases, dct)
class Operation(object):
__metaclass__ = LoginDecorator
def delete(self, x):
print 'deleted %s' % str(x)
def main():
op = Operation()
op.delete('test')
if __name__ == '__main__':
main()
这样就可以不用在delete函数上面写@login_required,也能达到decrator的效果了。