1.python 的metaclass

关于元类 在python中,有一经典解释文章,特别是注意到,而python中的基本元类就是type,type会根据传入参数的个数来决定发挥打印类型的函数功能或者发挥元类的作用。元类的自定义使用__metaclass__:

(一)自定义metaclass

使用metaclass只需声明:

__metaclass__ = something..

  放置元类的位置可以是任意的,python解释器进行查找元类的顺序这篇文章中已经详细说明:“如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。”值得注意的是,放置位置带来的区别。

放置于:模块级(module level)、类级(class level)、子类或者父类中。。<稍后讲到>

那么作为创建类的类(类工厂),metaclass可以是哪些“东西”呢?

  1. 函数作为metaclass
  2. 类作为metaclass

1).定义函数做为metaclass的情况:

def metafunc(classname,bases,attrs):
    for key,value in attrs.items():
            if isinstance(value,int):
                attrs[key.upper()]=value
                attrs.pop(key)
    print 'In[%s:From-->%s]'%(classname,bases)
    return type(classname,bases,attrs)

函数metafunc是定义好的作为__metaclass__元类属性的函数,其作用便是将要创建的类中的int类型的属性名大写。其实也就是在类创建前“做些事”之后,再利用type创建想要的类。值得注意的是:

1 #此时如果将元类属性放置在module level,发现无任何作用,不知道是不是python版本不同导致
 2 __metaclass__ = metafunc
 3 class MyClass(object):   
 4     lower = 123
 5     strs = 'string in MyClass..'
 6 class MySubClass(MyClass):
 7     kid_lower = 456
 8     kid_strs = 'string in MySubClass..'
 9 #此时若把元类属性放置于父类MyClass或者子类MySubClass中,发现只有放置了__metaclass__属性的类会达到预期的效果
10 class MyClass(object):
11     __metaclass__ = metafunc
12     lower = 123
13     strs = 'string in MyClass..'
14 class MySubClass(MyClass):
15     __metaclass__ = metafunc
16     kid_lower = 456
17     kid_strs = 'string in MySubClass..'

 

若将__metaclass__只放置于父类MyClass中,子类并不会被影响...这个不知为何,得同时写进类里<环境:python 2.7.11 in win7 64bits>

【输出:】

放置于父类结果:

In[MyClass:From-->(<type 'object'>,)]

放置于子类的结果:

In[MySubClass:From-->(<class '__main__.MyClass'>,)]

 

2).定义类做为metaclass的情况:

class testMetaclass(type):
    def __new__(cls,name,bases,attrs):
        for key,value in attrs.items():
            if isinstance(value,int):
                attrs[key.upper()]=value
                attrs.pop(key)
        print 'In[%s:From-->%s]==>testMetaclass'%(name,bases)
        return super(testMetaclass,cls).__new__(cls,name,bases,attrs)
    def __call__(cls,*args):
        print 'call metaclass'
        return super(testMetaclass,cls).__call__(*args)

自定义类testMetaclass(一般定义元类的命名可以在类名末尾加上Metaclass :P)作为__metaclass__元类属性,在元类创建类的时候,调用__new__方法,要在创建前“做些事”也是在这个方法中实现。__call__方法是创建的类实例化的时候才会被调用。这个也可以在实例化前“做些事”..:)

值得注意的是:

#若把__metaclass__属性放置于module level,也是任何作用都没有。。囧。但是把其只放置于父类Myclass中,子类也会被影响;把其只放置于子类中,父类不会被影响。
class MyClass(object):
    __metaclass__ = testMetaclass
    lower = 123
    strs = 'string in MyClass..'
class MySubClass(MyClass):
    kid_lower = 456
    kid_strs = 'string in MySubClass..'

【输出:】

放于父类的结果:

1 In[MyClass:From-->(<type 'object'>,)]==>testMetaclass
2 In[MySubClass:From-->(<class '__main__.MyClass'>,)]==>testMetaclass

放于子类的结果:

In[MySubClass:From-->(<class '__main__.MyClass'>,)]==>testMetaclass

  

(二)我用metaclass干嘛啊? 

一般情况下,如果要对方法或者属性进行使用前的“额外操作”,可以使用decorator 或者 descriptor ,对于动态的创建类也是有很多方法可以实现,不过,metaclass可以让我们在想要对类创建前进行各种操作需求的时候方便的提供工作。

  • 拦截类的创建,对传入的要创建的类的各项参数进行操作;<如果在module level可以进行类的拦截,那....>
  • 筛选要创建的类,进行针对性的操作。
  • 检查接口API
  • 等等等