欢迎访问宙启技术站
智能推送

Python装饰器详解:自定义函数增强的高级技巧

发布时间:2023-06-20 00:29:39

Python装饰器是一种高级技巧,它可以让我们在不改变函数原有逻辑的情况下,为函数添加新的功能或者改变函数的行为。装饰器本身就是一个函数,它接受一个函数对象作为参数,并返回一个新的函数对象,新的函数对象可能会在执行前或者执行后添加新的操作。本文将详细介绍Python装饰器的用法和实现。

1. 装饰器的基本用法

1) 装饰器的语法

Python装饰器采用了函数嵌套(closure)的概念,在定义一个装饰器函数时,需要在函数内部再定义一个函数,这里的内部函数就是装饰器函数。

装饰器函数的语法如下:

def decorator(func):
    def wrapper(*args, **kwargs):
        #操作1
        result = func(*args, **kwargs)
        #操作2
        return result
    return wrapper

这里的decorator就是装饰器函数,它有一个参数func,它是被修饰的函数对象。在decorator函数内部,定义了一个新的函数wrapper,它的作用是在被修饰函数func执行前或执行后添加一些新的操作。

wrapper函数有两个参数*args**kwargs,它们代表传递给被修饰函数func的任意位置参数和关键字参数,这样就可以支持任意类型的函数。

最后,decorator函数返回wrapper函数对象,这个函数对象可以覆盖原有的函数对象,从而实现对原有函数的扩展。

2) 装饰器的应用场景

Python装饰器广泛应用于日志记录、性能分析、缓存、权限校验等方面,具体用法如下:

- 日志记录

如下示例代码:

def log(func):
    def wrapper(*args, **kwargs):
        print(f'{func.__name__} called with args={args}, kwargs={kwargs}')
        return func(*args, **kwargs)
    return wrapper

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

add(3, 4) # output: add called with args=(3, 4), kwargs={}

这里的log函数就是一个装饰器函数,它将原有的add函数修饰后,每次调用add函数时,都会记录函数名、位置参数和关键字参数的信息。

- 性能分析

如下示例代码:

import time

def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f'{func.__name__} took {end - start} seconds')
        return result
    return wrapper

@timeit
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

fib(30) # output: fib took 0.3063042163848877 seconds

这里的timeit函数就是一个装饰器函数,它可以测量函数执行的时间,并输出执行时间的信息。

- 缓存

如下示例代码:

import functools

def cache(func):
    cache = {}
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        key = (args, tuple(sorted(kwargs.items())))
        if key in cache:
            print('cache hit')
            return cache[key]
        else:
            print('cache miss')
            result = func(*args, **kwargs)
            cache[key] = result
            return result
    return wrapper

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

add(2, 3) # output: cache miss
add(2, 3) # output: cache hit

这里的cache函数就是一个缓存函数,它可以缓存函数的输入和输出,避免重复计算。

- 权限校验

如下示例代码:

def login_required(func):
    def wrapper(*args, **kwargs):
        if 'user' in kwargs and kwargs['user']:
            return func(*args, **kwargs)
        else:
            print('You are not authorized!')
    return wrapper

@login_required
def add(a, b, user=None):
    return a + b

add(1, 2, user='Alice') # output: 3
add(3, 4) # output: You are not authorized!

这里的login_required函数就是一个权限校验函数,它可以根据用户信息授权,只允许已登录用户访问被修饰的函数。如果没有用户信息,则返回未授权的提示信息。

2. 装饰器的高级用法

1) 带参数的装饰器

有些情况下,装饰器可能需要接受一些参数,因此,需要定义一个新的函数来生成装饰器本身。这个新的函数通常称为“装饰器生成器”。

如下示例代码:

def repeat(num_times):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(num_times=3)
def greet(name):
    print(f'Hello, {name}')

greet('Alice') # output: Hello, Alice (printed 3 times)

这里的repeat函数是一个装饰器生成器,它接受一个整数参数num_times,并返回一个装饰器函数。decorator函数就是一个装饰器,它将被修饰函数执行多次,实现了greet函数的重复调用。

2) 多个装饰器的叠加

有些情况下,一个函数可能需要被多个装饰器叠加起来使用。可以将多个装饰器按照从上往下的顺序,分别应用于被修饰函数。

如下示例代码:

def debug(func):
    def wrapper(*args, **kwargs):
        print(f'Calling {func.__name__} with args={args}, kwargs={kwargs}')
        return func(*args, **kwargs)
    return wrapper

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f'{func.__name__} took {end - start} seconds')
        return result
    return wrapper

@debug
@timer
def add(a, b):
    return a + b

add(3, 4) # output: Calling add with args=(3, 4), kwargs={} 
 add took 1.90734863281e-06 seconds

这里的debugtimer函数都是装饰器函数,当这两个装饰器按照从上往下的顺序应用时,先应用debug装饰器,在输出调用信息后,将被修饰的函数传递给timer装饰器,计算函数执行时间后返回结果。

3) 类作为装饰器

虽然装饰器函数是Python解释器支持的语言特性,但有时候,我们可能需要更灵活的装饰器方式,比如使用类来实现装饰器功能。

如下示例代码:

`

class log:

def __init__(self, func):

self.func = func

def __call__(self, *args, **kwargs):

print(f'{self.func.__name__} called with args={args}, kwargs={kwargs}')

return self.func(*args, **kwargs)

@log

def add(a, b):

return a + b

add(3, 4) # output: add called with args=(3,