#函数装饰器是一个接收函数作为参数并返回一个新函数的高阶函数。它可以在不修改原函数代码的情况下,为函数添加额外功能。
"""
函数装饰器是Python中一个非常强大和有用的特性,它允许在不修改原函数代码的情况下,给函数增加新的功能。装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。
下面我将从基本概念开始,逐步讲解如何创建和使用装饰器,包括带参数的装饰器和类装饰器。
1.基本装饰器
首先,我们来看一个最简单的装饰器例子,它会在函数执行前后打印一些信息。
2.使用@语法糖
在Python中,我们可以使用@符号来应用装饰器,这是一种更简洁的写法。
3.装饰器带参数
有时候我们需要装饰器本身可以接受参数,这样我们可以根据参数的不同来调整装饰器的行为。
4.使用类作为装饰器
除了函数,类也可以被用来创建装饰器。类装饰器通常通过实现__call__方法来实现。
5.多个装饰器
一个函数可以同时使用多个装饰器,它们的应用顺序是从下往上(或从内到外)。
6.使用functools.wraps
在定义装饰器时,最好使用functools.wraps来保留原函数的元信息(如函数名、文档字符串等)。
"""
# @decorator()
# def function():
# pass
# # 等价于
# def function():
# pass
# function = decorator(function)
# --------------------------------------------------------------------------
# 1.定义1个装饰器
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
# 2.使用装饰器
@my_decorator
def say_hello():
print("Hello!")
# 3.调用函数
say_hello()
# --------------------------------------------------------------------------
# 装饰带参数的函数
# --------------------------------------------------------------------------
def timer(func):
def wrapper(*args,**kwargs):
import time
start = time.time()
result = func(*args,**kwargs)
end = time.time()
print(f"函数{func.__name__}执行耗时:{end-start:.4f}秒")
return result
return wrapper
@timer
def calculate_sum(n):
return sum(range(n))
print(calculate_sum(10000))
# ------------------------------------------------------------------------
#带参数的装饰器
# ------------------------------------------------------------------------
"""
目标:我们想要一个可以指定重复次数的装饰器,比如重复执行某个函数多次。
首先,repeat 是一个装饰器工厂,它接收一个参数 times,然后返回一个装饰器(也就是返回一个函数,这个函数将用来装饰目标函数)。
decorator 是真正的装饰器,它接收一个函数 func 作为参数(就是我们要装饰的函数)。
在 decorator 内部,我们定义了一个包装函数 wrapper,它负责执行 times 次 func,并返回最后一次的结果。
最后,repeat 返回 decorator。
当我们使用 @repeat(times=3) 时,实际上发生了以下步骤:
调用 repeat(times=3),返回 decorator 函数。
然后,将这个返回的 decorator 应用到 greet 函数上,即 greet = decorator(greet)。
现在,greet 实际上已经变成了 wrapper 函数。当我们调用 greet("Alice") 时,实际上是在调用 wrapper("Alice"),而 wrapper 内部会循环3次调用原来的 greet 函数。
注意:这个装饰器有一个问题,它只返回最后一次调用的结果。如果被装饰的函数没有返回值,那么循环3次后,result 会是 None,然后返回 None。如果函数有返回值,那么前两次的返回值会被覆盖,只保留最后一次的返回值。
装饰器工厂:repeat(times) 是一个函数,接收参数,返回真正的装饰器 decorator
装饰器:decorator(func) 接收要装饰的函数,返回包装函数 wrapper
包装函数:wrapper(*args, **kwargs) 实际被调用的函数,它包含额外的逻辑(重复执行)
闭包作用:wrapper 可以访问外层函数的变量 times 和 func,即使外层函数已经执行完毕
"""
def repeat(times):
"""装饰器工厂:返回一个装饰器"""
def decorator(func):
def wrapper(*args,**kwargs):
for _ in range(times):
result = func(*args,**kwargs)
# 仅返回最后一次调用的结果
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello,{name}")
return 0
greet("Alice")
# -----------------------------------------------------------------------
# 改进
# -----------------------------------------------------------------------
def repeat(times):
def decorator(func):
def wrapper(*args,**kwargs):
results = []
for _ in range(times):
results.append(func(*args,**kwargs))
return results
return wrapper
return decorator
# ------------------------------------------------------------------------
# 类装饰器
# ------------------------------------------------------------------------
class CountCalls:
def __init__(self,func):
self.cfunc = func
self.ccalls = 0
def __call__(self, *args, **kwargs):
self.ccalls += 1
print(f"第{self.ccalls}次调用{self.cfunc.__name__}")
return self.cfunc(*args,**kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
say_hello()
# ---------------------------------------------------------------------
# 多个装饰器
# ---------------------------------------------------------------------
def decorator1(func):
def wrapper():
print("装饰器1-前")
func()
print("装饰器1-后")
return wrapper
def decorator2(func):
def wrapper():
print("装饰器2 - 前")
func()
print("装饰器2 - 后")
return wrapper
@decorator1
@decorator2
def my_function():
print("原函数")
my_function()
# 输出:
# 装饰器1 - 前
# 装饰器2 - 前
# 原函数
# 装饰器2 - 后
# 装饰器1 - 后
# ---------------------------------------------------
# 类装饰器
# ---------------------------------------------------
"""
类装饰器是一个接收类作为参数并返回修改后的类的装饰器。它使用类来实现装饰器功能,通常通过实现__init__ 和__call__方法。
"""
class ClassDecorator:
def __init__(self,cls):
"""初始化,接收被装饰的类"""
self.cls = cls
def __call__(self, *args, **kwargs):
"""当装饰后的类被实例化时调用"""
return isinstance
@ClassDecorator
class MyClass:
pass
# 等价于
MyClass = ClassDecorator(MyClass)
# 示例1:添加类属性/方法
class AddAttributes:
"""添加额外属性和方法的装饰器"""
def __init__(self,cls):
# 参数 指向 使用场景 装饰器
# self 类的实例 实例方法 无或@staticmethod
# cls 类本身 类方法 @classmethod
self.cls = cls
# 添加类属性
cls.version = "1.0.0"
cls.author = "system"
# 添加类方法
"""
lambda是创建匿名函数的快捷方式
def add(a,b):
return a+b
等价于
add = lambda a,b : a + b
对于实例方法,需要self参数:
normal_method = lambda self :self.name.upper()
----------------------------------------------
class Person2:
pass
# 添加方法时必须有self参数
Person2.say_hello = lambda self : f"Hello ,I am {self.name}"
p2 = Person2()
p2.name = "Mayer"
# Hello ,I am Mayer
print(p2.say_hello())
--------------------------------------------
class Myclass:
# 类属性
version = "2.0.0"
# cls 指向Myclass 这个类对象
# Myclass
print(Myclass.__name__)
# 2.0.0
print(Myclass.version)
# 在方法中访问
Myclass.get_info = lambda self:f"{self.__class__.__name__}v{self.__class__.version}"
obj = Myclass()
# MyClass v2.0.0
print(obj.get_info())
"""
cls.get_info = lambda self :f"{cls.__name__}v{cls.version}"
def __call__(self, *args, **kwargs):
return self.cls(*args,**kwargs)