对于装饰器来说,如果要使用那么@装饰器 .
如果现在1或N个类中的所有方法全要使用装饰器,怎么办呢. 这时元类就能帮上忙了,元类是创建类的一个类,可以在创建类的过程中把所有的装饰器安装在所有的类方法上.
另下面代码中有一个错误的使用方法
首先是正常的装饰器语法:
import time
from types import FunctionType
#一个用于计时的简单装饰器
def timer(label = '',tracer = True):
def decorator(func):
def on_call(*args,**kwargs):
start = time.clock()
result = func(*args,**kwargs)
now = time.clock()
elapsed = now - start
on_call.alltime += elapsed
if tracer:
format = '%s%s: %.5f %.5f'
formatvalues = (label,func.__name__,elapsed,on_call.alltime)
print(format%formatvalues)
return result
on_call.alltime = 0
return on_call
return decorator
class Person:
def __init__(self,name='0x',age = 1):
self.name = name
self.age = age
@timer() #正常的装饰器语法
def lastName(self):
return self.name.split()[-1]
p = Person()
p.lastName()
混合:
#先创建一个metaclass , 继承type
#重定义__new__ , 实际参数是(meta,classname,supers,classdict)
#循环classDict,把方法重定向, 相当于使用了装饰器
class MetaPerson(type):
def __new__(cls, *args, **kwargs):
print('元类本身:',cls)
print('类的命名空间词典:',args[2])
classDict = args[2]
for attr,value in classDict.items():
if type(value) is FunctionType:
classDict[attr] =timer()(value)
print('类创建完成')
return type.__new__(cls,*args,**kwargs)
class Person(metaclass=MetaPerson):
def __init__(self,name='0x',age = 1):
self.name = name
self.age = age
#@timer() #正常的装饰器语法
def lastName(self):
return self.name.split()[-1]
此时有人会说, 这种遍历class中所有的函数, 并直接调用装饰器 也可以直接调用一个普通函数来完成啊,
错误的使用方式:
#普通的类
class A(object):
def __init__(self):
print("A init")
def post(self):
print("post")
def get(self):
print("get")
#装饰器
def tr(func):
call = 0
def oncall(*args, **kwargs):
nonlocal call
call += 1
print("call : " , call)
return func(*args,**kwargs)
return oncall
#一个用于设置装饰器的函数
def set_dec(cls, func):
for k,v in cls.__dict__.items():
if type(v) is types.FunctionType:
cls.__dict__[k] = func(v) #在这里会直接报错
if __name__ == '__main__':
set_dec(A,tr) #直接报错
#报错:
cls.__dict__[k] = func(v)
TypeError: 'mappingproxy' object does not support item assignment
会报错的原因是除了在元类__new__中的dict可修改, 对class.__dict__ 获取的命名空间都是只读的.
如果想上面的函数成功,可修改成这样:
def set_dec(cls, func):
for k,v in cls.__dict__.items():
if type(v) is types.FunctionType:
setattr(cls,k,func(v))
进阶一下 , 混合应用:
#装饰器与元类的混合应用2
#上面例子中只能使用一个装饰器
#可以用利用闭包来增加一个装饰器参数
def decorator_methods(decorator): #接受一个装饰器
class MetaClass(type): #一个元类
def __new__(cls, *args, **kwargs):
print('元类:',cls)
classDict = args[2]
for attr, value in classDict.items():
if type(value) is FunctionType: #只要是一个函数的话, 可以自己判断额外的
classDict[attr] = decorator(value) #使用装饰器,装饰方法
return type.__new__(cls,*args,**kwargs)
return MetaClass
class Person(metaclass=decorator_methods(timer())):
def __init__(self,name='0x',age = 1):
self.name = name
self.age = age
#@timer() #正常的装饰器语法
def lastName(self):
return self.name.split()[-1]
p = Person()
p.lastName()
同样的上述方法也可以使用装饰器来实现:
#同样的可以使用装饰器来实现
def deco(decorator): #接受一个装饰器
def on_deco(aClass): #接受一个类对象.
for attr, value in aClass.__dict__.items():
if type(value) is FunctionType:
setattr(aClass,attr,decorator(value)) #注意,这里使用setattr, 不要使用aClass.__dict__ 来赋值.
return aClass #因为这里类对象已经创建完了
return on_deco
@deco(timer())
class Person:
def __init__(self,name='0x',age = 1):
self.name = name
self.age = age
#@timer() #正常的装饰器语法
def lastName(self):
return self.name.split()[-1]
p = Person()
p.lastName()