尽管Tim Peters说99%的程序猿都不需要用到元类Metaclasses,但是我们还是要对它有一个大致的了解,知道它的机制和作用,万一以后用到了呢?
Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why). – Tim Peters
meta这个词源于希腊,wikipedia中解释的是生成抽象概念背后的模板(indicate a concept which is an abstraction behind another concept, used to complete or add to the latter)。我们知道在Python中一切都是对象,对象都是类实例化的结果,其实类也是对象,那它是谁实例化的结果呢?顾名思义metaclass就是创建类的模板,也就是创建类对象的类,我们称它为元类。因此总的关系可以归纳为metaclass实例化产生class,class实例化的结果是instance,另一个问题来了,元类也是对象,那它是由谁实例化得到的?这里会有鸡生蛋、蛋生鸡的问题,一直下去就没有尽头了,那么在Python中这个追溯终止在type类,即元类是type类或其子类,而type类的元类就是它自己。
接下来我们介绍下type类。一般我们使用Python内置的type()方法来查看对象的类型,比如1的类型是int,"1"的类型是str,当对象是类时,我们看到的类型是type,也就是元类。
print(type(1)) ——>>
print(type("1")) ——>>
#查看创建ObjectCreator类的类
class ObjectCreator(object):
pass
print(type(ObjectCreator)) ——>>
print(type(ObjectCreator())) ——>>
其实type()方法根据输入参数的不同会有不同的功能,在只有一个参数时,即type(object),返回的是创建该参数的类型,在有三个参数时返回的是新的类型对象,因此我们可以使用type类实例化创建一个新类。type创建类时,参数格式为type(classname, parentclasses , attrs),其中classname是类名(字符串类型),parentclasses是类所有父类(元组类型),attrs是类的所有{属性:值}。
当我们使用关键字class定义一个类时,Python解释器会自动解释为type方法创建类,当然我们也可以直接使用type方法创建类,两者的效果相同。
class Foo(object):
bar = True
Foo = type('Foo', (), {'bar':True})
print(Foo) ——>>
print(Foo.bar) ——>> True
f = Foo()
print(f) ——>> <__main__.Foo object at 0x8a9b84c>
print(f.bar) ——>> True
我们使用继承的方式创建类,将FooChild继承Foo,关键字class和type方法两者的效果相同。也可以定义一个方法,用type方法给类增加属性。
class FooChild(Foo):
pass
FooChild = type('FooChild', (Foo,), {})
print(FooChild) ——>>
print(FooChild.bar) ——>> True
def echo_bar(self):
pass
FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
print hasattr(Foo, 'echo_bar') ——>> False
print hasattr(FooChild, 'echo_bar') ——>> True
my_foo = FooChild()
print dir(my_foo) ——>> ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'echo_bar']
以上可以理解为类的创建基本过程,类的创建最终都会调用type.new(cls, classname, bases, attrs),这样会在堆中创建一个类对象并返回,cls就是该类的元类,name是类名,bases是基类/超类,attrs是dict类型的属性,可以是变量,也可以是方法。我们可以在创建类的过程中修改name、bases、attrs的值来灵活的达到我们的目的。因此我们可以在类中添加__metaclass__属性,用它指定创建该类的元类,需要注意的是元类必须为type类或其子类我们可以对某个类一直调用__class__,在有限次之后,会发现它返回的就是type类。
age = 35
print age.__class__ ——>>
name = 'bob'
print name.__class__ ——>>
def foo(): pass
print foo.__class__ ——>>
class Bar(object): pass
b = Bar()
print b.__class__ ——>>
print age.__class__.__class__ ——>>
print name.__class__.__class__ ——>>
print foo.__class__.__class__ ——>>
print b.__class__.__class__ ——>>
想必大家也看出来用__metaclass__指定元类的好处就是可以在创建类之前或之后修改类的属性以控制类的生成过程。大多数情况下我们无需指定__metaclass__,因为解释器会按一定的策略找到元类,元类的参数就是我们用class关键字定义类时的类名,类的所有父类,以及类中定义的{属性名:属性对象}。