美文网首页
python拾遗3 - 装饰器

python拾遗3 - 装饰器

作者: 天命_风流 | 来源:发表于2020-05-04 00:44 被阅读0次

什么是装饰器

所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。

Decorators is to modify the behavior of the function through a wrapper so we don’t have to actually modify the function.

装饰器可以存在的四个原因

  • 函数是对象,可以把函数赋予变量:
def func(message):
    print('Got a message: {}'.format(message))
    
send_message = func
send_message('hello world')

# 输出
Got a message: hello world
  • 函数可以当做参数:
def get_message(message):
    return 'Got a message: ' + message


def root_call(func, message):
    print(func(message))
    
root_call(get_message, 'hello world')

# 输出
Got a message: hello world
  • 函数内可以嵌套函数:
def func(message):
    def get_message(message):
        print('Got a message: {}'.format(message))
    return get_message(message)

func('hello world')

# 输出
Got a message: hello world
  • 函数的返回值可以是函数:
def func_closure():
    def get_message(message):
        print('Got a message: {}'.format(message))
    return get_message

send_message = func_closure()
send_message('hello world')

# 输出
Got a message: hello world

装饰器的本质

def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper

def greet():
    print('hello world')

greet = my_decorator(greet)
greet()

# 输出
wrapper of decorator
hello world

其中,greet = my_decorator(greet) 可以改为 @my_decorator,这也就是装饰器的本质。

可以接受任意参数的装饰器

由于你不知道要用装饰器装饰什么函数,以及这些函数都有些怎样不同的参数,所以你需要使用 *args 和 **kwargs 接受任意参数:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper

为装饰器定义参数

有时,我们要为装饰器指定参数:

def repeat(num):
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(num):
                print('wrapper of decorator')
                func(*args, **kwargs)
        return wrapper
    return my_decorator


@repeat(4)
def greet(message):
    print(message)

greet('hello world')

# 输出:
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world

原函数变形的问题

使用装饰器装饰过的函数会产生变化(接上面的代码):

greet.__name__
## 输出
'wrapper'

help(greet)
# 输出
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)

可以发现,函数被装饰以后,它的信息改变了。为了解决这个问题,我们通常使用内置的装饰器 @functools.warp 解决,它可以帮助保留原函数的元信息:

import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper
    
@my_decorator
def greet(message):
    print(message)

greet.__name__

# 输出
'greet'

类装饰器

我们可以使用类的 __call__( ) 方法实现装饰器的功能(但是这有一定的问题,所以不推荐使用这种方式):

class Count:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print('num of calls is: {}'.format(self.num_calls))
        return self.func(*args, **kwargs)

@Count
def example():
    print("hello world")

example()

# 输出
num of calls is: 1
hello world

example()

# 输出
num of calls is: 2
hello world
...

装饰器的嵌套

装饰器也可以嵌套使用,下面两块代码有相同的效果:

@decorator1
@decorator2
@decorator3
def func():
    ...
decorator1(decorator2(decorator3(func)))

你可以看下面的这个例子帮助理解:

import functools

def my_decorator1(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator1')
        func(*args, **kwargs)
    return wrapper


def my_decorator2(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator2')
        func(*args, **kwargs)
    return wrapper


@my_decorator1
@my_decorator2
def greet(message):
    print(message)


greet('hello world')

# 输出
execute decorator1
execute decorator2
hello world

装饰器用例

  • 身份认证:认证用户是否登陆
import functools

def authenticate(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        request = args[0]
        if check_user_logged_in(request): # 如果用户处于登录状态
            return func(*args, **kwargs) # 执行函数post_comment() 
        else:
            raise Exception('Authentication failed')
    return wrapper
    
@authenticate
def post_comment(request, ...)
    ...
  • 日志记录:记录函数的调用时间,并输出到日志
import time
import functools

def log_execution_time(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        res = func(*args, **kwargs)
        end = time.perf_counter()
        print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
        return res
    return wrapper
    
@log_execution_time
def calculate_similarity(items):
    ...
  • 输入合理性检查:检查输入的数据是否合法
import functools

def validation_check(input):
    @functools.wraps(func)
    def wrapper(*args, **kwargs): 
        ... # 检查输入是否合法
    
@validation_check
def neural_network_training(param1, param2, ...):
    ...
  • 缓存:pylru 模块为我们提供了缓存能力,我们可以用他缓存很多东西,例如:指定函数在指定参数下的计算结果
@lru_cache
def check(param1, param2, ...) # 检查用户设备类型,版本号等等,由于很多用户的设备和版本重复,所以我们可以使用缓存提升性能
    ...

元类(metaclass)

装饰器用于在函数创建前对函数进行装饰。同样,我们可以使用 元类(metaclass)在类创建前对类进行定制。
但是,这种定制是非常危险的,因为它会带来代码逻辑的改变。所以,从这个角度上来说,我们不推荐使用元类这个牛x 的功能。
当然,如果你觉得有必要,可以自己学习一下

相关文章

  • python拾遗3 - 装饰器

    什么是装饰器 所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。 Decorat...

  • python3基础---详解装饰器

    1、装饰器原理 2、装饰器语法 3、装饰器执行的时间 装饰器在Python解释器执行的时候,就会进行自动装饰,并不...

  • 让你Python到很爽的加速递归函数的装饰器

    Python技巧——好用的一个装饰器 今天我们会讲到一个[装饰器] 注记:链接“装饰器”指Python3教程中的装...

  • python装饰器

    最近在学习python3,对于python的装饰器,多线程以及异步IO有点卡顿。这两天在研究python的装饰器,...

  • 我终于弄懂了Python的装饰器(四)

    此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我...

  • 我终于弄懂了Python的装饰器(二)

    此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我...

  • 我终于弄懂了Python的装饰器(三)

    此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我...

  • 我终于弄懂了Python的装饰器(一)

    此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我...

  • 装饰器模式

    介绍 在python装饰器学习 这篇文章中,介绍了python 中的装饰器,python内置了对装饰器的支持。面向...

  • python装饰器

    在python3中 迭代器 生成器 装饰器 理解比较难 1、装饰器本质上是一个python函数,它可以让其他函数在...

网友评论

      本文标题:python拾遗3 - 装饰器

      本文链接:https://www.haomeiwen.com/subject/jpmsghtx.html