Python基础-11.装饰器
一、装饰器基础
装饰器(decorators)是Python中的一种高级功能,它允许你动态地修改函数或类的行为。
装饰器的应用场景:
- 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
- 性能分析: 可以使用装饰器来测量函数的执行时间。
- 权限控制: 装饰器可用于限制对某些函数的访问权限。
- 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。
基本语法
所谓的装饰器也就是一个函数,装饰器的意思就如字面意思一样,就是将一个函数进行装饰,添加一些特别的功能。
【示例】一个简单的装饰器使用代码段
# 构建一个装饰器函数
def demo(func):
def wrapper(*arg, **kwargs):
# 调用原始函数之前的代码功能
print("before call")
result = func(*arg, **kwargs)
# 调用原始函数之后的代码功能
print("after call")
return result
return wrapper
# 使用装饰器
@demo
def greet(name):
print("hello", name) # 原始函数的功能实现
greet('Jack') # 运行函数
解析:
1.上面创建了一个装饰器函数demo,他接收一个叫func类型为函数的参数,并返回一个内部函数wrapper。
2.内部函数wrapper在调用传入的函数前后新增了额外的功能行为,最后返回传入函数的值。
3.创建一个函数greet,在定义greet函数前面使用@demo,Python自动将greet作为参数传递给demo,然后返回的wrapper函数替换掉原来的demo。
总结:装饰器就是将一个简单的函数传入装饰器函数中,将函数重写,返回一个最终功能更完善的函数。
使用装饰器
装饰器通过@符号应用在函数定义之前。
使用装饰器,如下所示:
@decorator
def hello():
print("hello")
上面的代码就相当于以下代码:
# 定义hello函数
def hello():
print("hello")
# 将hello对象传入decorator装饰器,返回一个新函数重新赋值hello
hello = decorator(hello)
相当于将hello函数传递给decorator装饰器,将函数重写成完善的函数后返回赋值给hello,之后每次调用hello函数时,调用的是经过装饰器处理后的函数。
【示例】上面功能完整的装饰器示例,在执行函数前后增加了打印信息
# decorator装饰器函数
def decorator(func):
def wrapper():
print("before call") # 执行函数前打印信息
func() # 调用原始函数
print("after call") # 执行函数后打印信息
return wrapper # 返回新的函数
# 使用decorator装饰器传入hello函数
@decorator
def hello():
print("hello")
hello() # 调用新的hello函数
''' 运行结果如下
before call
hello
after call
'''
带参数的装饰器
1.原函数传递参数
如果原函数需要参数,可以在装饰器的wrapper函数中传递参数,因为返回的是wrapper对象,所以wrapper也要加入参数选项,func对象调用也要加入参数选项。
【示例】代码如下:
def decorator(func):
def wrapper(*arg, **kwargs):
print("before call")
result = func(*arg, **kwargs)
print("after call")
return result
return wrapper
# 使用decorator装饰器
@decorator
def hello(name):
print("hello", name)
hello('Jack')
''' 运行结果如下
before call
hello Jack
after call
'''
2.装饰器传递参数
装饰器本身也可以接受参数,装饰器传递参数的意思就是在使用装饰器时装饰器后面传入参数,如:@log("INFO"),我们只需要额外定义一个外层函数来接收函数即可。
【示例】代码如下:
def log(prefix): # 外层接收参数
def decorator(func): # 中层接收被装饰的函数
def wrapper(*args, **kwargs): # 内层执行逻辑
print(f'[{prefix}] — 函数开始执行...')
result = func(*args, **kwargs)
print('运行结果为:', result)
print(f"[{prefix}] — 函数执行结束...")
return wrapper
return decorator
# 使用装饰器并传递参数"INFO"
@log("INFO")
def add(a, b):
return a + b
# 使用装饰器并传递参数"DEBUG"
@log("DEBUG")
def multiply(a, b):
return a * b
# 调用装饰之后的函数
add(3, 4)
multiply(2, 5)
''' 运行结果如下
[INFO] — 函数开始执行...
运行结果为: 7
[INFO] — 函数执行结束...
[DEBUG] — 函数开始执行...
运行结果为: 10
[DEBUG] — 函数执行结束...
'''
二、装饰器进阶知识
类装饰器
除了可以创建函数装饰器,类也可以使用装饰器。类装饰器(Class Decorator)是一种用于动态修改类行为的装饰器,它接收一个类作为参数,并返回一个新的类或修改后的类。
类装饰器的作用:
- 添加/修改类的方法或属性
- 拦截实例化过程
- 实现单例模式、日志记录、权限检查等功能
1.函数形式
函数形式的类装饰器就是将装饰器写成一个函数,接收一个 类作为参数,然后返回修改后的类。
【示例】代码如下:
def decorator(cls):
class Wrapper:
def __init__(self, *args, **kwargs):
# 实例化原始类
self.wrapped = cls(*args, **kwargs)
def __getattr__(self, name):
# 拦截未定义的属性访问,转发给原始类
return getattr(self.wrapped, name)
def demo(self):
print(f"调用 {cls.__name__}.display()前")
self.wrapped.demo()
print(f"调用 {cls.__name__}.display()后")
return Wrapper
# 使用decorator装饰器,构建新的类
@decorator
class OurClass:
def demo(self):
print('OurClass类传入的新demo函数')
OurClass().demo() # 调用使用装饰器之后的类里的函数
''' 运行结果如下
调用 OurClass.display()前
OurClass类传入的新demo函数
调用 OurClass.display()后
'''
2.类形式
类形式的类装饰器,就是用一个 类来实现装饰器(本质上要让类实例可调用,所以要实现__call__方法)。
【示例】代码如下:
class MyDecorator:
def __init__(self, cls):
print("初始化装饰器")
self._cls = cls # 保存被装饰的类
def __call__(self, *args, **kwargs):
print("调用装饰器,创建实例")
return self._cls(*args, **kwargs)
# 使用MyDecorator装饰器
@MyDecorator
class Person:
def __init__(self, name):
self.name = name
p = Person("Jack")
print(p.name)
'''运行结果如下
初始化装饰器
调用装饰器,创建实例
Jack
'''
内置装饰器
就跟内置函数一样,Python也有很多内置装饰器。
内置装饰器列表:
@staticmethod:定义静态方法,方法不依赖实例 (self) 或类 (cls),可用于类里有一些工具函数,但逻辑上属于类,就可以用静态方法。@classmethod:定义 类方法,第一个参数是cls(类本身),可用于做“工厂方法”,用类本身创建对象。@property:把一个方法变成只读属性,可用于给用户暴露“像属性一样”的接口,但内部可能是方法计算。
【示例】创建一个类使用以上三个内置装饰器
class Demo:
population = 0 # 类变量
def __init__(self, name, age):
self._name = name
self._age = age
Demo.population += 1
# ----------- property 示例 -----------
@property
def name(self):
# 把方法当成属性使用
return self._name
# ----------- classmethod 示例 -----------
@classmethod
def how_many(cls):
# 返回当前人口数
return cls.population
# ----------- staticmethod 示例 -----------
@staticmethod
def is_adult(age):
# 判断是否成年
return age >= 18
# ---------------- 测试 ----------------
p1 = Demo("Jack", 20)
p2 = Demo("Tom", 15)
print(p1.name) # 使用 @property
print(Demo.how_many()) # 使用 @classmethod
print(Demo.is_adult(17)) # 使用 @staticmethod
''' 运行结果如下
Jack
2
False
'''
多个装饰器堆叠
可以将多个装饰器堆叠在一起,它们会按照从下到上的顺序依次应用。
【示例】代码如下:
# 第一个装饰器函数
def decorator1(func):
def wrapper(*args, **kwargs):
print("进入 decorator1")
result = func(*args, **kwargs)
print("退出 decorator1")
return result
return wrapper
#第二个装饰器函数
def decorator2(func):
def wrapper(*args, **kwargs):
print("进入 decorator2")
result = func(*args, **kwargs)
print("退出 decorator2")
return result
return wrapper
# 使用两个装饰器
@decorator1
@decorator2
def say_hello(name):
print(f"Hello {name}")
say_hello("Jack")
''' 运行结果如下
进入 decorator1
进入 decorator2
Hello Jack
退出 decorator2
退出 decorator1
'''