Python 类与元类的深度挖掘 II【经验】
上一篇解决了通过调用类对象生成实例对象过程中可能遇到的命名空间相关的一些问题,这次我们向上回溯一层,看看类对象本身是如何产生的。
我们知道type()方法可以查看一个对象的类型,或者说判断这个对象是由那个类产生的:
print(type(12)) print(type('python')) classA: pass print(type(A))
通过这段代码可以看出,类对象A是由type()产生的,也就是说type也可以用来产生新的对象,而且产生的是类对象,因此它是所有类对象的类:
print(type.__doc__) type(object_or_name,bases,dict) type(object)->theobject'stype type(name,bases,dict)->anewtype
class定义类的语法实际上转化为type(name,bases,dict),其中name参数为类的名字,bases为继承父类的元组,dict为类的属性和方法:
classA: pass #实际上等于 B=type('A',(),{}) print(A.__name__==B.__name__) True
理论上说这就是元类的意义,但从实际的角度出发显然使用class语法更方便、合理,而元类的实际意义则是通过继承type类来构造一个新的元类,并进行特定的操作以产生具有特定行为的类对象。这样看来它的本质与普通的类对象没有差异,只不过继承的是type类。
在生成实例时是通过调用__init__方法进行初始化的,而实际上在此之前会先调用__new__方法用于创建实例,再通过__init__初始化,就好像__new__负责声明变量,而__init__负责对声明的变量进行初始化一样。这里有一个规则是__new__(cls,)的返回值必须是cls参数的实例,否则__init__将不会触发,例如在enum.Enum的定义中,由于枚举类型是单例模式,因此在定义__new__的时候没有返回其实例,也就不会进行初始化:
classEnum: def__new__(cls,value): print(cls,value) returnvalue def__init__(self): print("Willnotbecalled!") e=Enum(1) <class'__main__.Enum'>1
通常情况下自己定义__new__需要通过调用父类的__new__方法创建一个cls的实例,同样在定义元类的时候则是调用上面提到的type的用法(因为元类继承自type):
classMetaEnum(type): def__new__(metaclass,name,base,attrs): print("Metaclass:{}\nName:{}\nParents:{}\nAttributes:{}".format(metaclass,name,base,attrs)) returnsuper().__new__(metaclass,name,base,attrs) classEnum(metaclass=MetaEnum): #Python2.7中定义元类的方法是使用__metaclass__变量 #[PEP3115](https://www.python.org/dev/peps/pep-3115/) #将Python3.0以后语法改为classCls(metaclass=Meta) test=0 Metaclass: Name:Enum Parents:() Attributes:{'__qualname__':'Enum','__module__':'__main__','test':0} 此时我们再来看Enum的类,已经不再是type而是其元类MetaEnum: type(Enum) __main__.MetaEnum
除了__new__方法之外,PEP3115还定义了__prepare__属性,用于设定初始化的命名空间(即type的第3个参数),还是以enum.Enum为例,我们需要限制枚举类型中属性名称不得重复使用,则可以通过元类限制类的行为:
#定义新的字典类,在赋值新的dict[k]=v时
#检查k是否重复
class_EnumDict(dict): def__init__(self): super().__init__() self.members=[] def__setitem__(self,k,v): ifkinself.members: raiseTypeError("Attemptedtoreusekey:'{}'".format(k)) else: self.members.append(k) super().__setitem__(k,v) classMetaEnum(type): @classmethod def__prepare__(metaclass,cls,bases): return_EnumDict() def__new__(metaclass,name,base,attrs): returnsuper().__new__(metaclass,name,base,attrs) classEnum(metaclass=MetaEnum): pass classColor(Enum): try: red=1 red=2 exceptTypeError:#这里没有使用aserr:的原因是? print("TypeErrorcatched") TypeErrorcatched
Python中一切皆为对象,所有的对象都是某一类的实例,或是某一元类的实例,type是自己的元类也是自己的实例