首页 > 编程语言 > 详细

python 装饰器

时间:2020-09-19 18:15:25      阅读:48      评论:0      收藏:0      [点我收藏+]

装饰器背景

装饰器的是一种AOP切面编程思想,可以将核心代码从冗长的业务代码中剥离出来,常见的打日志例子:

def log():
    import inspect
    print(f‘called by {inspect.stack()[1][3]}...‘)

def add():
    log()
    print(‘add...‘)

def multiplication():
    log()
    print(‘multiplication...‘)

if __name__ == ‘__main__‘:
    multiplication()
    add()
called by multiplication...
multiplication...
called by add...
add...

这里log()为了举例并不复杂,但是如果业务更复杂,可能会将核心代码add() multiplication()“淹没”且会重复写某些代码。而这就是出发点,孕育出了装饰器的思想。装饰器,从字面意思也可意会到其涉及想法,起到一个装饰的作用,而被装饰者必然是核心业务代码

装饰器小例

针对上面的例子,我们配合装饰器修改一下:

def wapper(fun):
    def log():  # 返回该方法, 该方法中包含 fun()——核心逻辑代码
        import inspect
        print(f‘called by {fun.__name__}...‘)
        fun()
    return log

def add():
    print(‘add...‘)

def multiplication():
    print(‘multiplication...‘)

if __name__ == ‘__main__‘:
    add = wapper(add)  # 返回被 wapper 包装过的 add 方法. 取成 add 的名字罢了
    mul = wapper(multiplication)

    add()  
    mul()
called by add...
add...
called by multiplication...
multiplication...

带参数的装饰器

那么同样的,如果需要带参数,也只要简单的修改一下:

def wapper(fun):
    def log(*args, **kwargs):
        import inspect
        print(f‘called by {fun.__name__}...‘)
        fun(*args, **kwargs)  # 根据传入的参数按个数前后顺序匹配 add(a,b,l) 参数
    return log

def add(a, b, l):
    print(f‘add...{a+b}‘)
    for i in l:
        print(i)


if __name__ == ‘__main__‘:
    add = wapper(add)

    add(4,7, [9,99])  # 调用wapper中的log函数
called by add...
add...11
9
99

照上个例子再套一层,其实没有什么意义,就是方便更加充分理解(●ˇ?ˇ●):
关于闭包nonlocal,可点击查看nonlocal, 查看闭包

def wapper(msg):
    def inner_wapper(fun):
        nonlocal msg # 闭包 
        msg += 1
        print(msg)
        def log(*args, **kwargs):
            import inspect
            print(f‘called by {fun.__name__}...‘)
            fun(*args, **kwargs)
        return log
    return inner_wapper

def add(a, b, l):
    print(f‘add...{a+b}‘)
    for i in l:
        print(i)
10
called by add...
add...11
9
99

@语法糖

了解了上文的几个例子,为了写的简便一些可以使用 @ ——只是为了写的更方便,其实我还是喜欢上文的更简单直观

@wapper(9)  # 添加装饰器
def add(a, b, l):
    print(f‘add...{a+b}‘)
    for i in l:
        print(i)

if __name__ == ‘__main__‘:
    add(4,7, [9,99])

类装饰器

类装饰器其实和上文的装饰器没有多大区别。如果A是一个函数对象,A()调用函数,如果是一个类对象A()自然调用__call__()方法。所以如下例子:

class wapper:
    def __init__(self, fun, msg):
        self.msg = msg
        self.fun = fun
        print(self.msg)

    def __call__(self, *args, **kwargs):
        msg, = args
        print(msg)
        def log(*args, **kwargs):
            print(f‘called by {self.fun.__name__}...‘)
            return self.fun(*args, **kwargs)
        return log

def add(a, b, l):
    print(f‘add...{a+b}‘)
    for i in l:
        print(i)

if __name__ == ‘__main__‘:
    add = wapper(add, ‘hi here1‘)(‘__call__ 调用中‘)
    print(‘=======‘)
    add(4,7, [9,99])
hi here1
__call__ 调用中
=======
called by add...
add...11
9
99

@形式的类装饰器

这种形式只需要注意,如果类装饰器本身不需要传入参数,如下 @wapper。执行过程:当调用main中的add方法时,先初始化wapper,可以看到打印了function的名字add,随后add(4,7, [9,99]) 其实执行__call__方法:

class wapper:
    def __init__(self, fun):
        self.fun = fun
        print(self.fun.__name__)

    def __call__(self, *args, **kwargs):
        print(‘hi here2‘)
        print(f‘called by {self.fun.__name__}...‘)
        return self.fun(*args, **kwargs)

@wapper
def add(a, b, l):
    print(f‘add...{a+b}‘)
    for i in l:
        print(i)

if __name__ == ‘__main__‘:
    add(4,7, [9,99])
add
hi here2
called by add...
add...11
9
99

另一种情况是,当wapper本身传入参数初始化:@wapper(...)初始化wapper, call__传入fun
这种情况可能看上去有点绕,如下:@wapper(‘hi here1‘)初始化,随后add(4,7,[9,99])先调用__call

class wapper:
    def __init__(self, msg):
        self.msg = msg
        print(self.msg)

    def __call__(self, fun):
        print(‘hi here2‘)
        def log(*args, **kwargs):
            print(f‘called by {fun.__name__}...‘)
            return fun(*args, **kwargs)
        return log

@wapper(‘hi here1‘)  # 传参数
def add(a, b, l):
    print(f‘add...{a+b}‘)
    for i in l:
        print(i)

if __name__ == ‘__main__‘:
    add(4,7, [9,99])
hi here1
hi here2
called by add...
add...11
9
99

参考

https://www.cnblogs.com/cicaday/p/python-decorator.html

python 装饰器

原文:https://www.cnblogs.com/KongHuZi/p/13696504.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!