基本说明
Python 装饰器是一种函数,它可以用来修改其他函数的功能。它是 Python 中的一项高级编程技术,也是 Python 中比较重要的语法之一。
简单来说,装饰器就是一个函数,它可以接受一个函数作为参数,并返回一个函数。通过在函数定义前使用 @ 符号和装饰器函数名,可以把该函数传递给装饰器函数,并将装饰器函数返回的函数对象赋值给该函数名。这样,在函数被调用时,实际上是先调用了装饰器函数,然后再调用被装饰的函数。
装饰器在 Python 中的应用非常广泛,它可以用来实现很多功能,比如:
- 计时器:可以用装饰器来记录函数的执行时间。
- 缓存:可以用装饰器来缓存函数的执行结果,避免重复计算。
- 认证和授权:可以用装饰器来对函数进行认证和授权,限制用户访问权限。
- 日志记录:可以用装饰器来记录函数的执行日志,便于排查问题。
- 重试:可以用装饰器来对函数进行重试,确保函数能够成功执行。
- 输入验证:可以用装饰器来对函数的输入参数进行验证,确保输入合法。
- 错误处理:可以用装饰器来对函数的错误进行处理,防止程序崩溃。
代码示例
以下是一些 Python 常用的装饰器示例:
- 计时器装饰器:可以用来统计函数的执行时间。
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print("Function {0} takes {1:.6f} seconds to execute.".format(func.__name__, end_time - start_time))
return result
return wrapper
@timer
def my_func():
time.sleep(1)
my_func()
- 缓存装饰器:可以用来缓存函数的结果,避免重复计算。
def cache(func):
cached_results = {}
def wrapper(*args, **kwargs):
cache_key = (args, tuple(kwargs.items()))
if cache_key in cached_results:
return cached_results[cache_key]
result = func(*args, **kwargs)
cached_results[cache_key] = result
return result
return wrapper
@cache
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
- 日志装饰器:可以用来记录函数的执行日志。
def log(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print("Function {0} was called with arguments {1} and {2}, and returned {3}.".format(
func.__name__, args, kwargs, result))
return result
return wrapper
@log
def my_func(x, y):
return x + y
my_func(1, 2)
- 并发执行装饰器:可以用来让多个函数并发执行。
import threading
def concurrent(func):
def wrapper(*args, **kwargs):
t = threading.Thread(target=func, args=args, kwargs=kwargs)
t.start()
return t
return wrapper
@concurrent
def my_func():
print("Function is running.")
t = my_func()
t.join()
在这个示例中,定义了一个装饰器函数 concurrent
,它接受一个函数作为参数,并返回一个新的函数 wrapper
。在 wrapper
函数内部,先创建一个新的线程,并将被装饰的函数传递给该线程。然后,通过 @concurrent
的语法糖来装饰函数 my_func
,使其具有并发执行的能力。当调用 my_func
函数时,实际上会先调用 concurrent
函数,然后创建一个新的线程,并在该线程中执行 my_func
函数。最终,可以通过 t.join()
等待该线程执行完成。
- 参数化装饰器:可以用来给装饰器传递参数。
def repeat(num):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello")
say_hello()
在这个示例中,定义了一个参数化装饰器函数 repeat
,它接受一个参数 num
,并返回一个新的装饰器函数 decorator
。在 decorator
函数内部,定义了一个新的函数 wrapper
,用来重复调用被装饰的函数。然后,通过 @repeat(3)
的语法糖来装饰函数 say_hello
,使其重复调用 3 次。当调用 say_hello
函数时,实际上会先调用 repeat(3)
函数,然后再调用返回的 decorator
函数,最终得到一个重复调用 3 次的 say_hello
函数。
- 类装饰器:可以用来装饰类和类的方法。
class LogDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Function {0} was called with arguments {1} and {2}.".format(
self.func.__name__, args, kwargs))
return self.func(*args, **kwargs)
@LogDecorator
def my_func(x, y):
return x + y
result = my_func(1, 2)
print(result)
在这个示例中,定义了一个类装饰器 LogDecorator
,它接受一个函数作为参数,并实现了 __init__
和 __call__
方法。在 __call__
方法内部,先输出函数的调用日志,然后再调用被装饰的函数,并返回函数的返回值。然后,通过 @LogDecorator
的语法糖来装饰函数 my_func
,使其具有记录函数调用日志的功能。当调用 my_func
函数时,实际上会先调用 LogDecorator
的 __init__
方法和 __call__
方法,最终输出函数的调用日志,并返回函数的返回值。
- 类方法装饰器:可以用来装饰类方法,并对类方法的调用进行处理。
class LogCalls:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
return self.__class__(self.func.__get__(instance, owner))
def __call__(self, *args, **kwargs):
print("Method {0} was called with arguments {1} and {2}.".format(
self.func.__name__, args, kwargs))
return self.func(*args, **kwargs)
class MyClass:
@LogCalls
def my_method(self, x, y):
return x + y
obj = MyClass()
result = obj.my_method(1, 2)
print(result)
在这个示例中,定义了一个类方法装饰器 LogCalls
,它接受一个方法作为参数,并实现了 __get__
和 __call__
方法。在 __get__
方法内部,先判断装饰器所属的对象是否为类,如果是类,则返回装饰器对象本身;如果是实例,则返回一个新的装饰器对象,用来处理方法的调用。在 __call__
方法内部,输出方法的调用日志,然后调用原始方法并返回其返回值。然后,通过 @LogCalls
的语法糖来装饰类 MyClass
的方法 my_method
,使其具有记录方法调用日志的功能。当调用 my_method
方法时,实际上会先调用 LogCalls
的 __get__
方法和返回的新方法对象的 __call__
方法,最终输出方法的调用日志,并返回方法的返回值。
- 参数检查装饰器:可以用来检查函数参数的类型和值是否符合要求。
def check_args(*types):
def decorator(func):
def wrapper(*args, **kwargs):
for i, arg in enumerate(args):
if not isinstance(arg, types[i]):
raise TypeError("Expected argument {0} to be {1}, but got {2}.".format(
i+1, types[i], type(arg)))
return func(*args, **kwargs)
return wrapper
return decorator
@check_args(int, str)
def my_func(x, y):
print("x = {0}, y = {1}".format(x, y))
my_func(1, "hello")
在这个示例中,定义了一个参数检查装饰器函数 check_args
,它接受多个参数类型作为参数,并返回一个新的装饰器函数 decorator
。在 decorator
函数内部,定义了一个新的函数 wrapper
,用来检查函数参数的类型和值是否符合要求。然后,通过 @check_args(int, str)
的语法糖来装饰函数 my_func
,使其具有参数检查的功能。当调用 my_func
函数时,实际上会先调用 check_args(int, str)
函数,然后再调用返回的 decorator
函数,最终检查函数参数的类型和值是否符合要求。
- 单例模式装饰器:可以用来实现单例模式,确保类只有一个实例对象。
def singleton(cls):
instances = {}
def getinstance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return getinstance
@singleton
class MyClass:
def __init__(self, x):
self.x = x
obj1 = MyClass(1)
obj2 = MyClass(2)
print(obj1.x)
print(obj2.x)
print(id(obj1))
print(id(obj2))
在这个示例中,定义了一个单例模式装饰器函数 singleton
,它接受一个类作为参数,并返回一个新的函数 getinstance
。在 getinstance
函数内部,先判断该类是否已经有一个实例对象,如果没有,则创建一个新的实例对象并返回;如果已经有一个实例对象,则直接返回该实例对象。然后,通过 @singleton
的语法糖来装饰类 MyClass
,使其具有单例模式的功能。当创建 MyClass
类的实例对象时,实际上会先调用 singleton
函数,然后再调用返回的 getinstance
函数,最终返回一个唯一的实例对象。
- 性能优化装饰器:可以用来优化函数的性能,比如缓存结果、避免重复计算等。
import time
def memoize(func):
cache = {}
def wrapper(*args, **kwargs):
key = args + tuple(sorted(kwargs.items()))
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return wrapper
@memoize
def my_func(x, y):
time.sleep(1)
return x + y
result1 = my_func(1, 2)
result2 = my_func(1, 2)
result3 = my_func(2, 3)
print(result1)
print(result2)
print(result3)
在这个示例中,定义了一个性能优化装饰器函数 memoize
,它接受一个函数作为参数,并返回一个新的函数 wrapper
。在 wrapper
函数内部,先计算函数的参数和关键字参数的哈希值,作为缓存的键。然后,判断该键是否已经存在于缓存中,如果存在,则直接返回缓存中的结果;如果不存在,则调用原始函数,并将计算结果保存到缓存中。然后,通过 @memoize
的语法糖来装饰函数 my_func
,使其具有缓存计算结果的功能。当调用 my_func
函数时,如果函数的参数和关键字参数与之前的调用相同,则实际上会直接返回缓存中的结果,从而避免重复计算,提高函数的性能。