元类可以用来自动为类的所有方法添加装饰,把所有使用的类注册到一个API,自动为类添加用户接口逻辑,

这个逻辑不会把类名重新绑定到一个装饰器可调用对象,而是把类自身的创建指向特定的逻辑

一、Python 3.0中以及在Python 2.6的新式类的特性

type是产生用户定义的类的一个类。

元类是type类的一个子类。

类对象是type类的一个实例,或一个子类。

实例对象产生字一个类。

二、Class语句协议

class创建一个类对象:

调用type对象来创建class对象:

class A: pass -> class =type(classname, superclasses, attributedict)

初始化类方法:

type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)

如下所示的类定义示例:

class Spam(Eggs): #Inherits from Eggs
data = 1 #Class data attribute
def meth(self, arg): #Class method attribute

passPython将会从内部运行嵌套的代码块来创建该类的两个属性(data和meth),然后在class语句的末尾调用type对象,产生class对象:

Spam= type('Spam', (Eggs,), {'data': 1, 'meth': meth, '__module__': '__main__'})

三、元类初识,元类的定义

3.1 元类声明:

正如我们刚才看到的,类默认是type类创建的。要告诉Python用一个定制的元类来创建一个类,直接声明一个元类来拦截常规的类创建调用。怎么做到这点,依赖于你使用哪个Python版本。在Python 3.0中,在类标题中把想要的元类作为一个关键字参数列出来:

#声明元类

class Spam(metaclass=Meta):

3.2 超类继承:

继承超类也可以列在标题中,在元类之前。例如,在下面的代码中,新的类Spam继承自Eggs,但也是Meta的一个实例并且由Meta创建:

#继承超类class Spam(Eggs, metaclass=Meta)

3.3 Mate的作用:

当以上这些方式声明的时候,创建类对象的调用在class语句的底部运行,修改为调用元类而不是默认的type:

class =Meta(classname, superclasses, attributedict)

由于元类是type的一个子类,所以type类的__call__把创建和初始化新的类对象的调用

委托给元类,如果它定义了这些方法的定制版本:

Meta.__new__(Meta, classname, superclasses, attributedict)
Meta.__init__(class, classname, superclasses, attributedict)
示例分析:class Spam(Eggs, metaclass=Meta)
data= 1
defmeth(self, arg):pass在这条class语句的末尾,Python内部运行如下的代码来创建class对象:
Spam= Meta('Spam', (Eggs,), {'data': 1, 'meth': meth, '__module__': '__main__'})

四、基本元类的创建

通过继承自type的__new__方法而运行。它通常执行所需的任何定制并且调用type的超类的__new__方法来创建并运行新的类对象:

classMeta(type):def __new__(meta, classname, supers, classdict):#Run by inherited type.__call__
return type.__new__(meta, classname, supers, classdict)

将元类接入元类钩子中以定制——由于元类在一条class语句的末尾调用,并且因为

type对象的__call__分派到了__new__和__init__方法

4.1 创建一个最基本的元类:

classMetaOne(type):def __new__(meta, classname, supers, classdict):print('In MetaOne.new:', classname, supers, classdict, sep='\n...')return type.__new__(meta, classname, supers, classdict)classEggs(object):pass
print('making class')class Spam(Eggs, metaclass=MetaOne): #Inherits from Eggs, instance of Meta
data = 1 #Class data attribute
def meth(self, arg): #Class method attribute
pass
print('making instance')
X=Spam()print('data:', X.data)
打印结果:
makingclassIn MetaOne.new:
...Spam
...(,)
...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': }
making instance
data:1

4.2 定制构建和初始化(__init__方法):

元类也可以接入__init__协议,由type对象的__call__调用:通常,__new__创建并返回了类对象,__init__初始化了已经创建的类。元类也可以用做在创建时管理类的钩子:

classMetaOne(type):def __new__(meta, classname, supers, classdict):print('In MetaOne.new:', classname, supers, classdict, sep='\n...')return type.__new__(meta, classname, supers, classdict)def __init__(Class, classname, supers, classdict):print('In MetaOne init:', classname, supers, classdict, sep='\n...')print('...init class object:', list(Class.__dict__.keys()))classEggs:pass
print('making class')class Spam(Eggs, metaclass=MetaOne): #Inherits from Eggs, instance of Meta
data = 1 #Class data attribute
def meth(self, arg): #Class method attribute
pass
print('making instance')
X=Spam()print('data:', X.data)
打印结果:
makingclassIn MetaOne.new:
...Spam
...(,)
...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': }
In MetaOne init:
...Spam
...(,)
...{'__module__': '__main__', '__qualname__': 'Spam', 'data': 1, 'meth': }
...initclass object: ['__module__', 'data', 'meth', '__doc__']
making instance
data:1

五、用元类重载类创建调用(细谈元类创建)

由于它们涉及常规的OOP机制,所以对于元类来说,也可能直接在一条class语句的末尾捕获创建调用,通过定义type对象的__call__。然而,所需的协议有点多:

classSuperMeta(type):def __call__(meta, classname, supers, classdict):print('In SuperMeta.call:', classname, supers, classdict, sep='\n...')return type.__call__(meta, classname, supers, classdict)class SubMeta(type, metaclass=SuperMeta):def __new__(meta, classname, supers, classdict):print('In SubMeta.new:', classname, supers, classdict, sep='\n...')return type.__new__(meta, classname, supers, classdict)def __init__(Class, classname, supers, classdict):print('In SubMeta init:', classname, supers, classdict, sep='\n...')print('...init class object:', list(Class.__dict__.keys()))classEggs:pass
print('making class')class Spam(Eggs, metaclass=SubMeta):
data= 1
defmeth(self, arg):pass
print('making instance')
X=Spam()print('data:', X.data)

当这段代码运行的时候,所有3个重新定义的方法都依次运行。这基本上就是type对象默认做的事情:

打印结果:

makingclassIn SuperMeta.call:
...Spam
...(,)
...{'__module__': '__main__', 'data': 1, 'meth': }
In SubMeta.new:
...Spam
...(,)
...{'__module__': '__main__', 'data': 1, 'meth': }
In SubMeta init:
...Spam
...(,)
...{'__module__': '__main__', 'data': 1, 'meth': }
...initclass object: ['__module__', 'data', 'meth', '__doc__']
making instance
data:1

六、实例与继承的关系

1.元类继承自type类。尽管它们有一种特殊的角色元类,但元类是用c l a s s语句编写的,并且遵从Python中有用的OOP模型。例如,就像type的子类一样,它们可以重新定义type对象的方法,需要的时候重载或定制它们。元类通常重新定义type类的__new__和__init__,以定制类创建和初始化,但是,如果它们希望直接捕获类末尾的创建调用的话,它们也可以重新定义__c a l l__。尽管元类不常见,它们甚至是返回任意对象而不是type子类的简单函数。

2.元类声明由子类继承。在用户定义的类中,metaclass=M声明由该类的子类继承,因此,对于在超类链中继承了这一声明的每个类的构建,该元类都将运行。

3.元类属性没有由类实例继承。元类声明指定了一个实例关系,它和继承不同。由于类是元类的实例,所以元类中定义的行为应用于类,而不是类随后的实例。实例从它们的类和超类获取行为,但是,不是从任何元类获取行为。从技术上讲,实例属性查找通常只是搜索实例及其所有类的__dict__字典;元类不包含在实例查找中。

自定义实例化对象不能调用元类的属性与方法:

classMetaOne(type):
abc = 10def __new__(meta, classname, supers, classdict): #Redefine type method
print('In MetaOne.new:', classname)return type.__new__(meta, classname, supers, classdict)deftoast(self):print('toast')class Super(metaclass=MetaOne): #Metaclass inherited by subs too
def spam(self): #MetaOne run twice for two classes
print('spam')class C(Super): #Superclass: inheritance versus instance
def eggs(self): #Classes inherit from superclasses
print('eggs') #But not from metclasses
X =C()
X.eggs()#Inherited from C
X.spam() #Inherited from Super
X.toast() #Not inherited from metaclass
print(X.abc) #Not inherited from attribute
当这段代码运行的时候,元类处理两个客户类的构建,并且实例继承类属性而不是元类属性:
打印结果:
In MetaOne.new: Super
In MetaOne.new: C
eggs
spam
AttributeError:'C' object has no attribute 'toast'AttributeError: 'C' object has no attribute 'abc'

七、执行扩展的元类的静态工作实例

像装饰器一样,我们在元类中做扩展,声明了元类的每个类都将统一且正确地扩展,并自动地接收未来做出的任何修改。如下的代码展示了这一点:

这个示例中的元类仍然执行相当静态的工作:把两个已知的方法添加到声明了元类的每个类

defeggsfunc(obj):return obj.value * 4
defhamfunc(obj, value):return value + 'ham'
classExtender(type):def __new__(meta, classname, supers, classdict):
classdict['eggs'] =eggsfunc
classdict['ham'] =hamfuncreturn type.__new__(meta, classname, supers, classdict)class Client1(metaclass=Extender):def __init__(self, value):
self.value=valuedefspam(self):return self.value * 2
class Client2(metaclass=Extender):
value= 'ni?'X= Client1('Ni!')print(X.spam())print(X.eggs())print(X.ham('bacon'))
Y=Client2()print(Y.eggs())print(Y.ham('bacon'))

打印结果:

两个客户类都使用新的方法扩展了,因为它们是执行扩展的元类的实例

Ni!Ni!
Ni!Ni!Ni!Ni!
baconham
ni?ni?ni?ni?
baconham

八、执行扩展的元类的动态工作实例

定制元类的动态行为:主体类也可以基于运行时的任意逻辑配置

classMetaExtend(type):def __new__(meta, classname, supers, classdict):ifsometest():
classdict['eggs'] =eggsfunc1else:
classdict['eggs'] =eggsfunc2ifsomeothertest():
classdict['ham'] =hamfuncelse:
classdict['ham'] = lambda *args: 'Not supported'
return type.__new__(meta, classname, supers, classdict)

九、基于类装饰器的扩展实例与元类管理的对比

类装饰器常常可以和元类一样充当类管理角色

类装饰器可以管理类和实例

元类可以管理类和实例,但是管理实例需要一些额外工作

defeggsfunc(obj):return obj.value * 4
defhamfunc(obj, value):return value + 'ham'
defExtender(aClass):
aClass.eggs= eggsfunc #Manages class, not instance
aClass.ham = hamfunc #Equiv to metaclass __init__
returnaClass
@Extenderclass Client1: #Client1 = Extender(Client1)
def __init__(self, value): #Rebound at end of class stmt
self.value =valuedefspam(self):return self.value * 2@ExtenderclassClient2:
value= 'ni?'X= Client1('Ni!') #X is a Client1 instance
print(X.spam())print(X.eggs())print(X.ham('bacon'))
Y=Client2()print(Y.eggs())print(Y.ham('bacon'))
打印结果:
Ni!Ni!
Ni!Ni!Ni!Ni!
baconham
ni?ni?ni?ni?
baconham

十、基于类装饰器的扩展实例与元类管理的对比2

10.1 类装饰器管理实例:

def Tracer(aClass): #On @ decorator
classWrapper:def __init__(self, *args, **kargs): #On instance creation
self.wrapped = aClass(*args, **kargs) #Use enclosing scope name
def __getattr__(self, attrname):print('Trace:', attrname) #Catches all but .wrapped
return getattr(self.wrapped, attrname) #Delegate to wrapped object
returnWrapper
@Tracerclass Person: #Person = Tracer(Person)
def __init__(self, name, hours, rate): #Wrapper remembers Person
self.name =name
self.hours=hours
self.rate= rate #In-method fetch not traced
defpay(self):return self.hours *self.rate
bob= Person('Bob', 40, 50) #bob is really a Wrapper
print(bob.name) #Wrapper embeds a Person
print(bob.pay())
打印结果:
Trace: name
Bob
Trace: pay
2000
10.2 元类管理实例:
def Tracer(classname, supers, classdict): #On class creation call
aClass = type(classname, supers, classdict) #Make client class
classWrapper:def __init__(self, *args, **kargs): #On instance creation
self.wrapped = aClass(*args, **kargs)def __getattr__(self, attrname):print('Trace:', attrname) #Catches all but .wrapped
return getattr(self.wrapped, attrname) #Delegate to wrapped object
returnWrapperclass Person(metaclass=Tracer): #Make Person with Tracer
def __init__(self, name, hours, rate): #Wrapper remembers Person
self.name =name
self.hours=hours
self.rate= rate #In-method fetch not traced
defpay(self):return self.hours *self.rate
bob= Person('Bob', 40, 50) #bob is really a Wrapper
print(bob.name) #Wrapper embeds a Person
print(bob.pay())
打印结果:
Trace: name
Bob
Trace: pay2000

十一、基于类装饰器的扩展实例与元类管理的对比3

装饰器弊端:我们在想要在类中跟踪的每个方法前面添加装饰语法,并且在不再想要跟踪的使用后删除该语法。如果想要跟踪一个类的每个方法,在较大的程序中,这会变得很繁琐

元类创建跟踪方法:在构建一个类的时候运行,它们是把装饰包装器添加到一个类方法中的自然地方。通过扫描类的属性字典并测试函数对象,我们可以通过装饰器自动运行方法,并且把最初的名称重新绑定到结果。其效果与装饰器的自动方法名重新绑定是相同的。

classMetaTrace(type):def __new__(meta, classname, supers, classdict):for attr, attrval inclassdict.items():if type(attrval) is FunctionType: #Method?
classdict[attr] = tracer(attrval) #Decorate it
return type.__new__(meta, classname, supers, classdict) #Make class
class Person(metaclass=MetaTrace):def __init__(self, name, pay):
self.name=name
self.pay=paydefgiveRaise(self, percent):
self.pay*= (1.0 +percent)deflastName(self):return self.name.split()[-1]
bob= Person('Bob Smith', 50000)
sue= Person('Sue Jones', 100000)print(bob.name, sue.name)
sue.giveRaise(.10)print(sue.pay)print(bob.lastName(), sue.lastName())

打印结果:

call1 to __init__call2 to __init__Bob Smith Sue Jones
call1to giveRaise110000.0call1to lastName
call2to lastName
Smith Jones

要对方法应用一种不同的装饰器,我们只要在类标题行替换装饰器名称,实用与多种装饰的方法:

from types importFunctionTypefrom mytools importtracer, timerdefdecorateAll(decorator):classMetaDecorate(type):def __new__(meta, classname, supers, classdict):for attr, attrval inclassdict.items():if type(attrval) isFunctionType:
classdict[attr]=decorator(attrval)return type.__new__(meta, classname, supers, classdict)returnMetaDecorateclass Person(metaclass=decorateAll(tracer)): #Apply a decorator to all
def __init__(self, name, pay):
self.name=name
self.pay=paydefgiveRaise(self, percent):
self.pay*= (1.0 +percent)deflastName(self):return self.name.split()[-1]
bob= Person('Bob Smith', 50000)
sue= Person('Sue Jones', 100000)print(bob.name, sue.name)
sue.giveRaise(.10)print(sue.pay)print(bob.lastName(), sue.lastName())
打印结果:
call1 to __init__call2 to __init__Bob Smith Sue Jones
call1to giveRaise110000.0call1to lastName
call2to lastName
Smith Jones
第一个接收了定时器的默认参数,第二个指定了标签文本:
class Person(metaclass=decorateAll(tracer)): #Apply tracer
class Person(metaclass=decorateAll(timer())): #Apply timer, defaults
class Person(metaclass=decorateAll(timer(label='**'))): #Decorator arguments

总结:

元类和类装饰器不仅常常可以交换,而且通常是互补的。它们都对于定制和管理类和实例对象,提供了高级但强大的方法,因为这二者最终都允许我们在类创建过程中插入代码。尽管某些高级应用可能用一种方式或另一种方式编码更好,但在很多情况下,我们选择或组合这两种工具的方法来使用。