一文带你精通Python装饰器

1.引入

首先我们来用一个例子来引出什么是装饰器以及为什么要使用它.
下面是定义的一个Python function,仅仅是print一行字段

def f1():
    print("This is a function 1")

那么此时有了新的需求,需要在函数被调用执行时输出当时的时间,着很简单只需要调用一下其他类库就可以,代码如下

from datetime import datetime


def f1():
    print(datetime.now())
    print("This is a function 1")

ok, 此时需要确实是达到了,但是假如我们除了f1()还有f2()、f3()…等等方法都需要增加这个需求呢?难道需要给每个方法都去加上这一条输出时间语句?显然这种解决方案是不合适的. 并且它违背了对修改封闭,对扩展开放的原则

因此, 为了解决有多个函数都需要输出被调用时间的需求,并且满足开闭原则, 我们可以使用Python 函数式的特性 专门编写一个函数用来打印时间.

from datetime import datetime


def f1():
    print("This is a function 1")


def f2():
    print("This is a function 2")


def print_current_time(func):
    print(datetime.now)
    func
    

print_current_time(f1())
print_current_time(f2())

上面的代码可以更好实现我们的需求, 这样做逻辑上是没问题的,但是我们调用的时候不再是调用真正的业务逻辑 f1,f2 函数,而是换成了 print_current_time 函数,这就破坏了原有的代码结构, 现在我们不得不每次都要把原来的那个 f 函数作为参数传递给 print_current_time 函数,那么有没有更好的方式的呢?当然有,答案就是装饰器。

2.装饰器

1.简单装饰器

首先来看看什么是简单装饰器

import time


def decorator(func):
    def wrapper():
        print(time.time())
        func()

    return wrapper


def f():
    print("This is a function")
f = decorator(f)
f()

decorator 就是一个装饰器,它一个普通的函数,它把执行真正业务逻辑的函数 f 包裹在其中,看起来像 f 被 decorator 装饰了一样,decorator 返回的也是一个函数,这个函数的名字叫 wrapper。
这个例子乍一看跟上一步使用的print_current_time方法没有太大区别,甚至没有print_current_time方法简单明了,别急再往下看看@语法糖

2.@语法糖

@ 符号是装饰器的语法糖,它放在函数开始定义的地方,这样就可以省略最后一步再次赋值的操作。

上面的例子我们可以进行一些改变,如下

import time


def decorator(func):
    def wrapper():
        print(time.time())
        func()

    return wrapper

@decorator
def f():
    print("This is a function")
f()

如上所示,有了 @ ,我们就可以省去f = decorator(f)
这一句了,直接调用 f()即可得到想要的结果。你们看到了没有,f() 函数不需要做任何修改,只需在定义的地方加上装饰器,调用的时候还是和以前一样,如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。

3.*args、**kwargs

可能有人问,如果我的函数 f 需要参数怎么办?很简单只需要同样的给装饰器中wrapper方法加上参数即可
比如:

import time


def decorator(func):
    def wrapper(func_name):
        print(time.time())
        func(func_name)

    return wrapper

@decorator
def f(func_name):
    print("This function name is" + func_name)


f('first')

同理,如果f方法接收的是多参数只需要将wrapper方法参数变为:*agrs即可

   def wrapper(*args):
       print(time.time())
       func(*args)

同样的,f方法如果还可以接收关键字参数,也只需将wrapper方法参数加上:**kw即可

    def wrapper(*args, **kw):
        print(time.time())
        func(*args, **kw)

4.@wraps

当我们使用了装饰器后,方法名会发现改变

@decorator
def f(func_name):
    print(f.__name__)

输出结果是:wrapper,说明此时f的方法名已经变成wrapper,如果想要让使用装饰器的方法名不改变,只需在wrapper方法上加上@wraps

from functools import wraps

def decorator(func):
    @wraps(func)
    def wrapper():
        func()

    return wrapper
@decorator
def f():
    print(f.__name__)

f()

其实仔细想想,为什么使用装饰器后方法名称会改变?
因为本来最后执行的方法就是wrapper,对比着简单装饰器,我们其实只是用@语法糖看起来是执行的f方法而已。

5.优化装饰器写法

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值