python装饰器

装饰器,就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需修改

1. 函数核心

在python中, 函数也是对象, 是一等公民(first-class citizen),。

  1. 把函数赋予变量。
def func(var):
    print('打印{}'.format(var))
    
message = func
message('信息')

# 输出
打印信息
  1. 把函数作为参数,传入另一个函数
def get_message(var_a):
    return '获取信息:' + var_a


def root_call(func, var_b):
    print(func(var_b))
    
# func 指向 get_message, var_b 指向 'hello'
root_call(get_message, 'hello')  

# 输出
获取信息:hello
  1. 函数里定义函数(函数的嵌套)
def func(var):
    def get_message(var):
        print('获取信息: {}'.format(var))
    return get_message(var)

func('hello')
# 变量vara 指向了 参数'hello'  func 函数返回了 get_message(vara)的执行

# 输出
获取信息: hello
  1. 函数的返回值也可以是函数对象(闭包)
def func():
    def get_message(var):
        print('获取信息: {}'.format(var))
    return get_message

message = func()  # message 变量 指向了 func() 执行体(返回值get_message),
message('hello')  # vara 指向了 message 带进来的参数.

# 输出
获取信息: hello

2. 简单的装饰器

# 对原函数输出添加问候
def wenhou(func):
    def wrapper():
        print('你好')
        func()
    return wrapper

def name():
    print('李白')

name = wenhou(name)  # func 指向函数 name,name 重新指向函数 wrapper
name() # name() 就等价于 wrapper() 有于 func 指向了原来的name 所以func() 输出 李白

print('='*30)
# 装饰器的常用写法
@wenhou # 使用语法糖的格式对函数添加功能
def name():
    print('杜甫')

name()
# 输出
你好
李白
==============================
你好
杜甫
2.1 带参数的装饰器

​ 如果原函数greet () 中有参数需要传递给装饰器,而另外一个函数也需要使用decorator 装饰器,但是新的函数有两个参数,该如何?

​ 通常情况下,我们会把*args和**kwargs,作为装饰器内部函数wrapper() 的参数。

​ *args 和 **kwargs 表示接受任意数量和类型的参数

def decorator(func):
    def wrapper(*args, **kwargs):
        print('hello')
        func(*args, **kwargs)
    return wrapper
2.2 带有自定义参数的装饰器

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

比如定义一个参数,表示装饰器内部函数被执行的次数,如下:

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


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

greet('xixi')

# 输出:
hello
xixi
hello
xixi
hello
xixi

3. 装饰器的用法实例

3.1 身份认证

你登录微信,需要输入用户名密码,然后点击确认,这样,服务器端便会查询你的用户名是否存在,是否和密码匹配等等。如果认证通过,你就可以顺利登录;如果不通过,抛出异常显示登陆失败。如下:

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

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

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

对一些文件进行合理性检查,避免因为文件格式不正确造成巨大开销

import functools

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

3.4 缓存

缓存装饰器的用法
LRU cache, 在 python 中 的表现形式是@lru_cache, @lru_cache 会缓存进程中的函数参数和结果,当缓存满了以后,会删除least recenly used 的数据。

@lru_cache
def check(param1, param2, ...) # 检查用户设备类型,版本号等等
    ...
posted @ 2019-10-31 03:33  乐乙  阅读(147)  评论(0)    收藏  举报