Python-装饰器(Decorator)详解

饭宝  金牌会员 | 2024-12-18 21:10:54 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 800|帖子 800|积分 2400


在python中,函数是一等公民,意味着函数可以像其他对象一样被赋值、通报参数、作为返回值等。装饰器的根本语法是使用@符号将一个函数作为参数通报给另一个函数(即装饰器)。被装饰的函数在被调用时,实际上会实验装饰器函数返回的新函数‌。简朴来说,装饰器就是一个返回函数的函数。使用轻便,直接在函数定义上方加上 @decorator 语法糖。

1装饰器的根本概念

装饰器是为相识耦代码的逻辑、进步代码的复用性,并且让函数和类的活动在不修改其原始代码的前提下得以扩展。能有效地将某些附加活动抽象出来,使得主业务逻辑(比方函数自己)保持轻便和专注。装饰器本质上是一个担当函数作为参数并返回一个新函数的函数。它常用于:



  • 在函数实验前后添加额外的活动。
  • 修改函数的输入或输出。
  • 为函数增加一些元数据。
装饰器的应用场景非常广泛,比方:日记记录、权限检查、性能计时、缓存等。装饰器的引入可以办理以下几个问题:

代码复用:装饰器可以将某些跨多个函数或类的功能(如日记、权限校验、缓存等)提取出来,以便于复用,制止重复代码。

增强代码的可维护性:装饰器使得附加功能与主功能分离,从而使代码更清晰,容易明白与维护。通过装饰器,修改或增加功能时无需修改原有函数或类的实现。

关注点分离(Separation of Concerns):装饰器让你可以将不同的功能分开处理,将与业务逻辑无关的操作(如日记记录、性能监控等)从核心业务代码中剥离出来。

机动性和扩展性:使用装饰器可以机动地震态修改函数或方法的活动,而无需修改函数自己的代码。

2装饰器的使用

2.1 根本结构

最简朴的装饰器形式如下:

  1. def decorator(func):
  2.     def wrapper():
  3.         print("Before function call")
  4.         func()  # 调用原函数
  5.         print("After function call")
  6.     return wrapper
  7. # 使用装饰器
  8. @decorator
  9. def say_hello():
  10.     print("Hello!")
  11. # 调用装饰后的函数
  12. say_hello()
复制代码
解释:



  • decorator 是一个装饰器函数,它吸收一个函数 func 作为参数。
  • wrapper 是一个新的函数,它在调用原函数 func() 之前和之后添加了自定义的活动。
  • @decorator 语法实际上是 say_hello = decorator(say_hello),即用装饰器修改 say_hello 函数。
输出:

Before function call

Hello!

After function call

2.2 带参数的装饰器

装饰器可以担当参数。为了让装饰器支持带参数的函数,我们必要在 wrapper 函数中使用 *args 和 **kwargs 来通报参数。

示例:

  1. def decorator(func):
  2.     def wrapper(*args, **kwargs):
  3.         print("Before function call")
  4.         result = func(*args, **kwargs)
  5.         print("After function call")
  6.         return result
  7.     return wrapper
  8. @decorator
  9. def add(a, b):
  10.     return a + b
  11. result = add(3, 5)
  12. print("Result:", result)
复制代码
解释:



  • wrapper 使用 *args 和 **kwargs 通报给被装饰函数,如许它就可以处理带有任意参数的函数。
  • 在 wrapper 中,原函数 func(*args, **kwargs) 被调用,并且它的结果被返回。
输出:

Before function call

After function callResult: 8

2.3 装饰器的嵌套

装饰器可以嵌套使用,即一个函数可以同时被多个装饰器修饰。

示例:多个装饰器

  1. def decorator1(func):
  2.     def wrapper(*args, **kwargs):
  3.         print("Decorator 1 - Before function call")
  4.         result = func(*args, **kwargs)
  5.         print("Decorator 1 - After function call")
  6.         return result
  7.     return wrapper
  8. def decorator2(func):
  9.     def wrapper(*args, **kwargs):
  10.         print("Decorator 2 - Before function call")
  11.         result = func(*args, **kwargs)
  12.         print("Decorator 2 - After function call")
  13.         return result
  14.     return wrapper
  15. @decorator1
  16. @decorator2
  17. def say_hello():
  18.     print("Hello!")
  19. say_hello()
复制代码
解释:



  • 装饰器的次序是从下到上的,即 @decorator2 起首应用,然后是 @decorator1。
  • 调用 say_hello() 时,会先实验 decorator2,然后是 decorator1。
输出:

Decorator 2 - Before function call

Decorator 1 - Before function call

Hello!

Decorator 1 - After function call

Decorator 2 - After function call

2.4 使用 functools.wraps 保持原函数的元数据

当我们使用装饰器时,装饰器内部的 wrapper 函数会替代原始函数。这意味着原函数的名称、文档字符串(docstring)等信息会丢失。为了制止这种情况,可以使用 functools.wraps 来保持原函数的元数据。

示例:使用 functools.wraps

  1. import functools
  2. def decorator(func):   
  3.     @functools.wraps(func)
  4.     def wrapper(*args, **kwargs):
  5.         print("Before function call")
  6.         result = func(*args, **kwargs)
  7.         print("After function call")
  8.         return result
  9.     return wrapper
  10. @decorator
  11. def say_hello():
  12.     """This is the say_hello function."""
  13.     print("Hello!")
  14. print(say_hello.__name__)  # 输出:say_hello
  15. print(say_hello.__doc__)  # 输出:This is the say_hello function.
复制代码
解释:



  • @functools.wraps(func) 保证了装饰器不改变原函数的名称、文档字符串等元数据。
  • 如果没有使用 wraps,则 say_hello.__name__ 会返回 wrapper,而不是 say_hello。
2.5 带参数的装饰器

有时我们大概盼望为装饰器通报参数(比方动态调整装饰器的活动)。为了实现这一点,我们必要使用三层嵌套函数

示例:

  1. def decorator_with_args(arg):
  2.     def decorator(func):
  3.         def wrapper(*args, **kwargs):
  4.             print(f"Decorator argument: {arg}")
  5.             return func(*args, **kwargs)
  6.         return wrapper
  7.     return decorator
  8. @decorator_with_args("Hello")
  9. def greet(name):
  10.     print(f"Greetings, {name}!")
  11. greet("Alice")
复制代码
解释:



  • decorator_with_args 是一个担当参数的装饰器,它返回一个装饰器函数 decorator。
  • decorator 是实际的装饰器,它担当一个函数 func,并在 wrapper 函数中添加新的功能。
  • @decorator_with_args("Hello") 等价于 greet = decorator_with_args("Hello")(greet)。
输出:

Decorator argument: Hello

Greetings, Alice!

2.6 类方法和静态方法的装饰器

在类中使用装饰器时,必要注意装饰器的作用对象(类方法、实例方法、静态方法)。对于实例方法,装饰器会多一个 self 参数,对于类方法会有 cls 参数。

示例:装饰器作用于实例方法

  1. class MyClass:
  2.     def decorator(func):
  3.         def wrapper(self, *args, **kwargs):
  4.             print("Before calling instance method")
  5.             result = func(self, *args, **kwargs)
  6.             print("After calling instance method")
  7.             return result
  8.         return wrapper
  9.     @decorator
  10.     def greet(self, name):
  11.         print(f"Hello, {name}!")
  12. obj = MyClass()
  13. obj.greet("Alice")
复制代码
示例:装饰器作用于类方法和静态方法

  1. class MyClass:   
  2.     @staticmethod
  3.     def decorator(func):
  4.         def wrapper(*args, **kwargs):
  5.             print("Before calling static method")
  6.             result = func(*args, **kwargs)
  7.             print("After calling static method")
  8.             return result
  9.         return wrapper
  10.     @decorator   
  11.     @staticmethod
  12.     def greet(name):
  13.         print(f"Hello, {name}!")
  14. MyClass.greet("Alice")
复制代码
3、python内置装饰器

常见的有:


  • @staticmethod - 用于定义静态方法。
  • @classmethod - 用于定义类方法。
  • @property - 将方法转化为属性(getter)。
  • @functools.lru_cache - 用于缓存函数结果,提拔性能。
  • @functools.wraps - 保留被装饰函数的元数据。
  • @abstractmethod - 定义抽象方法。
  • @property.setter - 为属性添加 setter 方法。
  • @property.deleter - 为属性添加 deleter 方法。
3.1 @staticmethod

@staticmethod 装饰器用于将一个方法定义为静态方法。静态方法不必要访问实例 (self) 或类 (cls),它通常用于一些与类自己干系但不必要访问实例属性或方法的功能。

示例:

  1. class MyClass:   
  2.     @staticmethod
  3.     def greet(name):
  4.         print(f"Hello, {name}!")
  5. # 静态方法不需要实例化类
  6. MyClass.greet("Alice")
复制代码
输出:

Hello, Alice!

3.2 @classmethod

@classmethod 装饰器用于将方法定义为类方法。类方法吸收类自己作为第一个参数(通常命名为 cls),而不是实例。它通常用于操作类的属性或提供一些与类自己干系的功能。

示例:

  1. class MyClass:
  2.     count = 0
  3.     @classmethod
  4.     def increment_count(cls):
  5.         cls.count += 1
  6.         print(f"Count: {cls.count}")
  7. MyClass.increment_count()  # 调用类方法
复制代码
输出:

Count: 1

3.3 @property

@property 装饰器用于将方法变为属性,这意味着该方法的调用方式就像访问属性一样,而不必要显式调用方法。常用于定义只读属性,大概为属性添加 getter 和 setter 方法。

示例:

  1. class Circle:
  2.     def __init__(self, radius):
  3.         self._radius = radius
  4.     @property
  5.     def radius(self):
  6.         return self._radius
  7.     @property
  8.     def area(self):
  9.         return 3.14 * self._radius ** 2
  10. c = Circle(5)
  11. print(c.radius)  # 直接访问属性
  12. print(c.area)  # 直接访问属性
复制代码
输出:

5

78.5

3.4 @functools.lru_cache

@lru_cache 是 functools 模块提供的一个装饰器,用于对函数的结果举行缓存。对于相同的参数,lru_cache 会返回缓存中的结果,从而制止重复计算,提拔函数的服从。LRU 代表 "Least Recently Used"(最少使用)。

示例:

  1. import functools
  2. @functools.lru_cache(maxsize=None)
  3. def fibonacci(n):
  4.     if n < 2:
  5.         return n
  6.     return fibonacci(n - 1) + fibonacci(n - 2)
  7. print(fibonacci(35))
  8. #@lru_cache 将自动缓存 fibonacci 函数的结果,避免重复计算,提高性能。
复制代码
3.5 @functools.wraps

@wraps 是 functools 模块中的一个装饰器,用于确保被装饰函数的元数据(如函数名、文档字符串等)在装饰后保持不变。当我们编写装饰器时,通常会使用 @wraps 来制止装饰器修改原函数的名称和文档字符串。

示例:

  1. import functools
  2. def decorator(func):   
  3.     @functools.wraps(func)
  4.     def wrapper(*args, **kwargs):
  5.         print("Before function call")
  6.         return func(*args, **kwargs)
  7.     return wrapper
  8. @decorator
  9. def greet(name):
  10.     """This is the greet function"""
  11.     print(f"Hello, {name}!")
  12. print(greet.__name__)  # 输出:greet
  13. print(greet.__doc__)   # 输出:This is the greet function
复制代码
输出:

Before function call

Hello, Alice!

@functools.wraps 保证了 greet 函数的名称和文档字符串没有被 wrapper 函数覆盖。

3.6 @abstractmethod

@abstractmethod 是 abc 模块提供的装饰器,用于声明一个方法为抽象方法。抽象方法不能直接在基类中实现,而必须在子类中实现。它通常用于定义抽象基类(ABC,Abstract Base Classes)。

示例:

  1. from abc import ABC, abstractmethod
  2. class Shape(ABC):   
  3.     @abstractmethod
  4.     def area(self):
  5.         pass
  6. class Circle(Shape):
  7.     def __init__(self, radius):
  8.         self.radius = radius
  9.     def area(self):
  10.         return 3.14 * self.radius ** 2
  11. # 创建实例时,必须实现抽象方法
  12. circle = Circle(5)
  13. print(circle.area())
复制代码
输出:

78.5

3.7 @property.setter

@property.setter 装饰器用于给 @property 装饰器定义的属性添加 setter 方法。通过 setter 方法,可以控制属性值的设置。

示例:

  1. class Circle:
  2.     def __init__(self, radius):
  3.         self._radius = radius
  4.     @property
  5.     def radius(self):
  6.         return self._radius
  7.     @radius.setter
  8.     def radius(self, value):
  9.         if value <= 0:
  10.             raise ValueError("Radius must be positive.")
  11.         self._radius = value
  12. circle = Circle(5)
  13. print(circle.radius)  # 访问属性
  14. circle.radius = 10  # 设置属性
  15. print(circle.radius)  # 访问更新后的属性
复制代码
输出:

5

10

3.8 @property.deleter

@property.deleter 装饰器用于定义删除属性的方法,允许通过 del 删除对象的属性。

示例:

  1. class Circle:
  2.     def __init__(self, radius):
  3.         self._radius = radius
  4.     @property
  5.     def radius(self):
  6.         return self._radius
  7.     @radius.deleter
  8.     def radius(self):
  9.         print("Deleting radius")
  10.         del self._radius
  11. circle = Circle(5)
  12. print(circle.radius)  # 访问属性
  13. del circle.radius     # 删除属性
复制代码
输出:

5

Deleting radius

3.9 @staticmethod 与 @classmethod 的组合使用

有时我们会将 @staticmethod 和 @classmethod 与其他装饰器结合使用。比方,可以在类方法中使用 @classmethod 与 @staticmethod 组合,在方法中实验类和实例的操作。

4、 装饰器的实际作用示例

以下是一些装饰器常见的应用场景以及它们如何影响代码活动。

4.1 日记记录

假设你必要为多个函数添加日记记录功能,可以通过装饰器来制止重复编写日记记录代码。

  1. def log_decorator(func):
  2.     def wrapper(*args, **kwargs):
  3.         print(f"Calling function {func.__name__} with arguments {args} and keyword arguments {kwargs}")
  4.         result = func(*args, **kwargs)
  5.         print(f"Function {func.__name__} returned {result}")
  6.         return result
  7.     return wrapper
  8. @log_decorator
  9. def add(a, b):
  10.     return a + b
  11. @log_decorator
  12. def multiply(a, b):
  13.     return a * b
  14. add(1, 2)
  15. multiply(3, 4)
复制代码
输出:

Calling function add with arguments (1, 2) and keyword arguments {}

Function add returned 3

Calling function multiply with arguments (3, 4) and keyword arguments {}

Function multiply returned 12

装饰器使得日记记录功能从业务逻辑中分离开来,你只必要为函数加上 @log_decorator 装饰器,而不必要在每个函数中重复编写日记干系代码。

4.2 性能监控

如果你盼望为多个函数监控实验时间,可以通过装饰器轻松实现:

  1. import time
  2. def time_decorator(func):
  3.     def wrapper(*args, **kwargs):
  4.         start_time = time.time()
  5.         result = func(*args, **kwargs)
  6.         end_time = time.time()
  7.         print(f"Function {func.__name__} took {end_time - start_time} seconds to execute")
  8.         return result
  9.     return wrapper
  10. @time_decorator
  11. def slow_function():
  12.     time.sleep(2)
  13. @time_decorator
  14. def fast_function():
  15.     time.sleep(0.5)
  16. slow_function()
  17. fast_function()
复制代码
输出:

Function slow_function took 2.002345085144043 seconds to execute

Function fast_function took 0.5001745223999023 seconds to execute

装饰器主动记录了函数的实验时间,制止了在每个函数内部手动写 time.time() 计算实验时间的代码。

4.3 权限验证

在 web 开发中,经常必要在处理请求之前验证用户的权限。这时可以使用装饰器来为多个视图函数或 API 接口加上权限检查。

  1. def permission_required(func):
  2.     def wrapper(*args, **kwargs):
  3.         if not has_permission():
  4.             raise PermissionError("You do not have permission to access this resource.")
  5.         return func(*args, **kwargs)
  6.     return wrapper
  7. def has_permission():
  8.     # 假设检查某个用户是否有权限
  9.     return False
  10. @permission_required
  11. def sensitive_data():
  12.     return "This is sensitive data."
  13. try:
  14.     sensitive_data()
  15. except PermissionError as e:
  16.     print(e)
复制代码
输出:

You do not have permission to access this resource.

装饰器为 sensitive_data 函数增加了权限检查逻辑。如许,后续所有必要权限验证的函数都可以通过添加 @permission_required 来使用该功能。

4.4 缓存

常见的缓存装饰器可以缓存函数的返回值,制止重复计算:

  1. from functools import lru_cache
  2. @lru_cache(maxsize=None)  # 使用内置的缓存装饰器
  3. def expensive_function(x):
  4.     print(f"Calculating {x}...")
  5.     return x * 2
  6. print(expensive_function(4))  # 计算并缓存结果
  7. print(expensive_function(4))  # 从缓存中获取结果
复制代码
输出:

Calculating 4...

8

8

第一次调用时计算并缓存,第二次调用直接返回缓存结果,节省了计算时间。

装饰器是 Python 中的一种用于修改函数或方法活动的高级特性,功能强大且机动,这里例举了一些装饰器的原理、应用和代码示例。感兴趣的小伙伴欢迎留言进一步探究。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

饭宝

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表