Bob's Blog

Web开发、测试框架、自动化平台、APP开发、机器学习等

返回上页首页

Python中的装饰器及其用法



python程序中经常能看到在函数上有类似@decorator的写法,这就是一个装饰器,它可以帮我们把代码写得更简洁更精炼。

@property和@classmethod你一定非常熟悉;如果你用过pytest的话,那么@pytest.mark.hookwrapper和@pytest.hookimpl(hookwrapper=True)你一定不会陌生。

装饰器可以做为一个统一的增强或者修改函数的方式,而不改变原函数的功能和参数。

最简单的装饰器

比如现在有一个输出当前时间的函数:

import datetime

def record_time_now():
    print(datetime.datetime.now())

record_time_now()

假设我们需要在输出时间之前输出当前用户名,那么在print(datetime.datetime.now())上一行加一句即可。

但是如果现在有许多的函数需要修改,并且根据不同的条件输出不同的信息,那么就得在每一个函数里做修改,这样很增加很多冗余的代码。

于是我们可以用装饰器写明需要在函数之外做什么事,再指定该装饰器到函数即可。

import datetime

def show_log(func):
    def inner():
        print("---show log here---")
        func()
    return inner

@show_log
def record_time_now():
    print(datetime.datetime.now())

record_time_now()

这里在输出时间之前先输出一句提示。

当函数需求参数时,装饰器应该这么写

往往函数是需要传递参数的,对于这种,装饰器也需要指明,但参数可以用args和kwargs来代替,不用一个个显式写明。

比如如下代码在加法和除法前后输出了当前时间:

import datetime

def show_time(func):
    def wrapper(*args, **kwargs):
        print(datetime.datetime.now())
        f = func(*args, **kwargs)
        print(datetime.datetime.now())
        return f
    return wrapper

@show_time
def add(a, b):
    return a + b

@show_time
def divide(a, b):
    return a / b

print(add(3, 5))
print(divide(99, 11))

装饰器也可以有参数

在我们给函数指定装饰器时,也可以传递参数给装饰器,以达成不同条件下的适应。

比如:

def logging(level):
    def wrapper(func):
        def inner(*args, **kwargs):
            print(f"{level}: ---now '{func.__name__}' get a {level} log---")
            return func(*args, **kwargs)
        return inner
    return wrapper

@logging(level="Debug")
def add(a, b):
    return a + b

@logging(level="Info")
def divide(a, b):
    return a / b

print(add(3, 5))
print(divide(99, 11))


## output:
# Debug: ---now 'add' get a Debug log---
# 8
# Info: ---now 'divide' get a Info log---
# 9.0

多个装饰器的执行顺序

多个装饰器装饰的顺序是从里到外(就近),而调用的顺序是从外到里(就远)。

比如:

def decorator1(func):
    print("outer decorator1 top")
    def wrapper():
        print("innter decorator1 top")
        func()
        print("inner decorator1 bottom")
    print("outer decorator1 bottom")
    return wrapper

@decorator1
def temp():
    print("this is temp function")

temp()


## output:
# outer decorator1 top
# outer decorator1 bottom
# innter decorator1 top
# this is temp function
# inner decorator1 bottom

当只有一个装饰器时,是先执行wrapper之外的语句,然后再依次执行wrapper里的语句。

此时加上一个装饰器:

def decorator1(func):
    print("outer decorator1 top")
    def wrapper():
        print("innter decorator1 top")
        func()
        print("inner decorator1 bottom")
    print("outer decorator1 bottom")
    return wrapper

def decorator2(func):
    print("outer decorator2 top")
    def wrapper():
        print("innter decorator2 top")
        func()
        print("inner decorator2 bottom")
    print("outer decorator2 bottom")
    return wrapper

@decorator2
@decorator1
def temp():
    print("this is temp function")

temp()


## output:
# outer decorator1 top
# outer decorator1 bottom
# outer decorator2 top
# outer decorator2 bottom
# innter decorator2 top
# innter decorator1 top
# this is temp function
# inner decorator1 bottom
# inner decorator2 bottom

此时先执行隔temp函数最近的decorator1的wrapper外,再执行decorator2的wrapper外;接着再执行decorator2的wrapper内,再是decorator1的wrapper内。可以注意output的最后五行,可以看出decorator2是把temp加上decorator1一起做了装饰。

下一篇:  用Django做一个简单的记账网站(一)环境搭建
上一篇:  用Flutter做一个记账APP(一)环境搭建

共有0条评论

添加评论

暂无评论