python编程Python已看

python装饰器

2021-11-18  本文已影响0人  D_w

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

函数核心回顾

  1. python中函数也是对象,可以赋予变量
def func(message):
    print('Got a message: {}'.format(message))
    
send_message = func
send_message('hello world')      # 相当于调用func('hello world')

# 输出
Got a message: hello world
  1. 函数当做参数传给另一个函数
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
  1. 函数中定义函数,也就是函数的嵌套
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
  1. 函数的返回值也可以是函数对象(闭包)
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

这里的函数 my_decorator() 就是一个装饰器,它把真正需要执行的函数 greet() 包裹在其中,并且改变了它的行为,但是原函数 greet() 不变。
上述代码在 Python 中有更简单、更优雅的表示:

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

@my_decorator     # 相当于greet=my_decorator(greet)
def greet():
    print('hello world')

greet()

若是函数带参数也要使用装饰器,则装饰器需要写成如下形式:

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

通常情况下,我们会把args和kwargs,作为装饰器内部函数 wrapper() 的参数。args和*kwargs,表示接受任意数量和类型的参数, args 可变参数,转化为tuple **kwargs 关键参数,转化为dict

带有自定义参数的装饰器

装饰器还有更大程度的灵活性。刚刚说了,装饰器可以接受原函数任意类型和数量的参数,除此之外,它还可以接受自己定义的参数。

def repeat(num):          # 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

上述的函数被装饰器修饰后并不再是原函数,而变为wrapper函数,要解决这个问题,我们通常使用内置的装饰器@functools.wrap,它会帮助保留原函数的元信息。

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(),每当你调用一个类的示例时,函数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

...

装饰器的嵌套

Python 也支持多个装饰器,比如写成下面这样的形式:

@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, ...)
    ...
日志记录

在实际工作中,如果你怀疑某些函数的耗时过长,导致整个系统的 latency(延迟)增加,所以想在线上测试某些函数的执行时间,那么,装饰器就是一种很常用的手段。

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):
    ...
输入合理性检查

比如检查函数去取json、Yaml文件的合法性

import functools

def validation_check(input):
   @functools.wraps(func)
   def wrapper(*args, **kwargs): 
       ... # 检查输入是否合法
   
@validation_check
def neural_network_training(param1, param2, ...):
   ...
上一篇下一篇

猜你喜欢

热点阅读