Python@装饰器(函数+类装饰器)


装饰器是修改其他函数的功能的函数

函数

Python 中可以将函数传递给一个变量

def func():
    print("say hello")

a = func
print(a) # <function func at 0x0000025F271C6DC0>
a()  # say hello
func() # say hello

删除原函数看看会发生什么

del func
print(a) # <function func at 0x0000025F271C6DC0>
print(func) # name 'func' is not defined

我们还可以在函数内部定义一个函数,并将其赋值给其他变量(函数名也当做变量)

def func():

    def say():
        print("say hello")
    return say

def not_say():
    print("say goodbye")

not_say = func()
print(not_say) # <function func.<locals>.say at 0x0000025F271C68B0>
not_say() # say hello

可以看到,我们通过函数调用,将函数 say 赋值给了函数 not_say

第一个装饰器

def func_decorator(a_func):
 
    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")
 
        a_func()
 
        print("I am doing some boring work after executing a_func()")
 
    return wrapTheFunction
 
def my_func():
    print("I am the function which needs some decoration to remove my foul smell")
 
my_func()
#outputs: "I am the function which needs some decoration to remove my foul smell"
 
my_func = func_decorator(my_func)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()
 
my_func()
#outputs:I am doing some boring work before executing a_func()
#        I am the function which needs some decoration to remove my foul smell
#        I am doing some boring work after executing a_func()

这就是 Python 装饰器做的事情,他们封装一个函数,并且用这样或那样的方式来修改它的行为,那么用 @ 符号如何表示呢?

@func_decorator
def my_func():
    """Hey you! Decorate me!"""
    print("I am the function which needs some decoration to "
          "remove my foul smell")
 
my_func()

#the @func_decorator is just a short way of saying:
# my_func = func_decorator(my_func)

@func_decorator 其实就是 my_func = func_decorator(my_func),功能是改变当前函数的功能(这里是 my_func

但是这里有一个问题,这样做相当于将原还是 my_func 覆盖了,直接变成了 wrapTheFunction

print(my_func.__name__) # wrapTheFunction

这并不是我们想要的,我们只想改变原函数的功能,仍希望能够访问原函数的其他属性

  • Python提供了函数 functools.wraps
    • @wraps 接受一个函数来进行装饰,并加入了复制函数名称,注释文档,参数列表等功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性
from functools import wraps
def func_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction():
        print("I am doing some boring work before executing a_func()")
        a_func()
        print("I am doing some boring work after executing a_func()")
        
    return wrapTheFunction

@func_decorator
def my_func():
    """Hey you! Decorate me!"""
    print("I am the function which needs some decoration to "
          "remove my foul smell")
 
print(my_func.__name__) # my_func

应用

日志(Logging)

from functools import wraps
 
def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging
 
@logit
def addition_func(x):
   """Do some math."""
   return x + x
 
 
result = addition_func(4)
# Output: addition_func was called

类作装饰器

要使用类作为装饰器,需要在类内定义__call__方法。

  • __call__ 是一种 magic method,可以将该类的实例像函数一样被调用
class Person:
    def __init__(self) -> None:
        self.num = 10
        self.name = "nsy"
    
    def __call__(self):
        print(self.num)
    
a = Person()
a() # 10, 这里就是调用了 __call__ 方法

我们将类作为装饰器

class Person:
    def __init__(self, func) -> None:
        self.num = 10
        self.name = "nsy"
        self.func = func
    
    def __call__(self, *args):
        print("Process before my func")
        self.func(*args)
        print("Process after my func")
        
@Person
def say_name(name): # say_name = Person(say_name), 实例化了一个对象
    print(f"my name is {name}")

say_name("nsy") # 调用 __call__ 方法
  • 这里实际上还是相当于 say_name = Person(say_name),这就创建了一个 Person 对象实例,之后我们在调用 say_name("nsy") 时就是调用了 __call__ 方法,这样就将类作为装饰器了

参考:Python 函数装饰器 | 菜鸟教程 (runoob.com)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长命百岁️

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值