元类与装饰器的混合应用

本文探讨了如何在Python中利用元类在创建类时批量应用装饰器,以避免为每个类方法手动添加装饰器。文章指出,通过元类的`__new__`方法可以修改类的字典,将装饰器应用于所有类方法。同时,文章还指出了直接修改`class.__dict__`的错误做法,并提供了解决方案,最后展示了装饰器实现的相同功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

元类基础

装饰器基础

对于装饰器来说,如果要使用那么@装饰器 .

如果现在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()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值