python--装饰器

1. 简介

装饰器是Python中非常强大的一个特性,它是一种将已有函数进行封装的技术。在不改变原函数的情况下,通过装饰器可以为函数添加新的功能。装饰器本质上是一个函数,它可以接收一个函数作为参数,返回一个经过包装后的函数,这个包装后的函数通常也可以接收函数的所有参数,并在此基础上完成新增的功能。

2. 基础语法

在Python中,装饰器是通过使用'@'语法糖来实现的,具体如下:

@decorator

def func():

pass

装饰器可以理解为一个函数的装饰物,它会将函数包装在自己的内部,从而实现对函数进行增强或者改变函数的行为。在上述代码中,'@decorator'就是一个装饰器,它需要一个函数作为参数,并返回一个函数。这里需要注意的是,装饰器必须返回一个函数,否则程序会报错。

3. 使用场景

3.1 计时器

在实际开发中,经常需要统计程序在执行时的时间,而此时可以使用装饰器来实现。下面的代码展示了如何通过装饰器实现一个简单的计时器。

import time

def timer(func):

def wrapper(*args, **kwargs):

start_time = time.time()

res = func(*args, **kwargs)

end_time = time.time()

print('time used:', end_time - start_time)

return res

return wrapper

@timer

def test_timer(n):

res = 0

for i in range(n):

res += i

return res

test_timer(10000000)

在上面的代码中,我们定义了一个装饰器'@timer',它将会为函数'test_timer'添加计时器功能,计算程序从开始到结束的时间并输出。最后调用'test_timer'函数,并传入参数'10000000'。

3.2 缓存

有时候,在处理一些计算密集型的任务时,可能会重复运行多次相同的函数。这时,我们可以使用装饰器来实现缓存,并将已经计算过的结果保存下来,避免重复计算。

def memoize(func):

cache = {}

def wrapper(*args):

if args in cache:

return cache[args]

res = func(*args)

cache[args] = res

return res

return wrapper

@memoize

def fibonacci(n):

if n < 2:

return n

return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(35))

在上面的代码中,我们定义了一个装饰器'memoize',它接受一个参数'func',返回一个包装函数'wrapper'。在'wrapper'函数中,我们通过一个字典来保存调用时的参数和结果,如果需要重新计算时,先查看字典中是否保存了该参数的结果,如果已经有了就直接返回结果,否则进行计算,并将结果保存到字典中。

3.3 日志系统

日志是一种非常重要的功能,可以记录系统运行时的事件和状态,方便排错和分析。使用装饰器可以非常方便地实现日志功能,下面的代码演示了如何使用装饰器为函数添加日志记录功能。

def logger(func):

def wrapper(*args, **kwargs):

print('arguments:', args, kwargs)

res = func(*args, **kwargs)

print('result:', res)

return res

return wrapper

@logger

def test_logger(a, b, c):

return a + b + c

print(test_logger(1, 2, c=3))

在上面的代码中,我们定义了一个装饰器'logger',它接收一个函数作为参数,并返回一个包装函数'wrapper'。在'wrapper'函数中,我们先输出函数调用时的参数,然后执行原来的函数,并将结果输出。

4. 多个装饰器的使用

在实际使用中,一个函数可能需要添加多个装饰器,例如需要添加计时器、缓存和日志功能。这时,我们可以使用多个装饰器进行组合。下面的代码演示了如何使用多个装饰器进行组合。

import time

def memoize(func):

cache = {}

def wrapper(*args):

if args in cache:

return cache[args]

res = func(*args)

cache[args] = res

return res

return wrapper

def logger(func):

def wrapper(*args, **kwargs):

print('arguments:', args, kwargs)

res = func(*args, **kwargs)

print('result:', res)

return res

return wrapper

def timer(func):

def wrapper(*args, **kwargs):

start_time = time.time()

res = func(*args, **kwargs)

end_time = time.time()

print('time used:', end_time - start_time)

return res

return wrapper

@logger

@memoize

@timer

def test(n):

res = 0

for i in range(n):

res += i

return res

test(10000000)

在上面的代码中,我们为函数'test'添加了三个装饰器,分别是'@logger'、'@memoize'和'@timer'。它们的执行顺序是由下往上的,因此先执行'timer'装饰器,然后执行'memoize'装饰器,最后执行'logger'装饰器。

5. 相关注意事项

5.1 不修改原函数参数和返回值

在编写装饰器时,需要保证不修改原函数的参数和返回值。因为如果在装饰器中修改这些值,就会影响到原函数的使用。

5.2 保留原函数的元信息

在使用装饰器时,需要保留原函数的元信息,例如函数名和文档字符串等。这可以通过使用Python内置的@functools.wraps来实现。

import functools

def logger(func):

@functools.wraps(func)

def wrapper(*args, **kwargs):

print('arguments:', args, kwargs)

res = func(*args, **kwargs)

print('result:', res)

return res

return wrapper

@logger

def test_logger(a, b, c):

'''This is a test function'''

return a + b + c

print(test_logger.__name__)

print(test_logger.__doc__)

在上面的代码中,我们在装饰函数'wrapper'前使用了@functools.wraps(func)语法糖。这可以使得装饰器内部的函数'wrapper'保留原函数的元信息,例如函数名和文档字符串。

5.3 装饰器带参数

除了可以使用单个装饰器,还可以为装饰器传递参数,这样可以在不同的场景下使用不同的参数进行装饰。下面的代码演示了如何使用带参数的装饰器。

def repeat(n):

def decorator(func):

def wrapper(*args, **kwargs):

for i in range(n):

res = func(*args, **kwargs)

return res

return wrapper

return decorator

@repeat(3)

def test_repeat(a, b):

return a + b

print(test_repeat(3, 4))

在上面的代码中,我们定义了一个带参数的装饰器'repeat',它接收一个参数'n',返回一个装饰器'decorator'。在'decorator'装饰器中,我们定义了一个新的函数'wrapper',并在其中通过循环来重复调用原函数。然后返回结果。

5.4 将装饰器定义为类

使用类来定义装饰器,可以使装饰器更加灵活和可扩展。

class Decorator(object):

def __init__(self, func):

self.func = func

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

print('arguments:', args, kwargs)

res = self.func(*args, **kwargs)

print('result:', res)

return res

@Decorator

def test_decorator(a, b, c):

'''This is a test function'''

return a + b + c

print(test_decorator(1, 2, c=3))

在上面的代码中,我们定义了一个名为'Decorator'的类,它包含了初始化方法'__init__'和特殊方法'__call__'。'__init__'方法接收一个函数作为参数,并保存到实例变量'func'中。'__call__'方法是当实例被调用时,会自动触发,其中我们通过'print'语句输出函数调用时的参数,并执行原函数,最后返回结果。

6. 总结

装饰器是Python中非常强大的一种特性,它可以为函数添加新的功能,且不允许修改原函数的行为。在实际开发中,我们可以使用装饰器来实现很多功能,例如计时器、缓存和日志记录等。此外,我们还可以为装饰器传递参数,实现在不同的场景下使用不同的参数进行装饰。除此之外,我们还可以通过将装饰器定义为类的方式,使其更加灵活和可扩展。希望本文可以帮助大家更好地理解和应用Python中的装饰器。

后端开发标签