Python全景系列之装饰器使用的全面讲解

作者:techlead_krischang 时间:2021-08-21 01:04:12 

Python 装饰器深入探讨

在 Python 中,装饰器提供了一种简洁的方式,用来修改或增强函数和类的行为。装饰器在语法上表现为一个前置于函数或类定义之前的特殊标记:

@simple_decorator
def hello_world():
   print("Hello, world!")

在这个例子中,simple_decorator 是一个装饰器,它作用于下方的 hello_world 函数。装饰器在概念上就像一个包装器,它可以在被装饰的函数执行前后插入任意的代码,进而改变被装饰函数的行为。

参数化装饰器

我们还可以进一步将装饰器参数化,这让装饰器的行为更具灵活性。比如,我们可以定义一个装饰器,让它在函数执行前后打印自定义的消息:

def message_decorator(before_message, after_message):
   def decorator(func):
       def wrapper(*args, **kwargs):
           print(before_message)
           result = func(*args, **kwargs)
           print(after_message)
           return result
       return wrapper
   return decorator

@message_decorator("Start", "End")
def hello_world():
   print("Hello, world!")

在这个例子中,message_decorator 是一个参数化装饰器,它接受两个参数,分别代表函数执行前后要打印的消息。

理解装饰器的工作原理

在 Python 中,函数是第一类对象。这意味着函数和其他对象一样,可以作为变量进行赋值,可以作为参数传给其他函数,可以作为其他函数的返回值,甚至可以在一个函数里面定义另一个函数。这个特性是实现装饰器的基础。

def decorator(func):
   def wrapper():
       print('Before function execution')
       func()
       print('After function execution')
   return wrapper

def hello_world():
   print('Hello, world!')

decorated_hello = decorator(hello_world)
decorated_hello()

在这个例子中,decorator 函数接收一个函数 hello_world 作为参数,并返回了一个新的函数 wrapped_func。这个新函数在 hello_world 函数执行前后分别打印一条消息。我们可以看到,装饰器实际上是一个返回函数的函数。

函数签名保持

默认情况下,装饰器会“掩盖”掉原函数的名字和文档字符串。这是因为在装饰器内部,我们返回了一个全新的函数。我们可以使用 functools.wraps 来解决这个问题:

import functools
def decorator(func):
   @functools.wraps(func)
   def wrapper():
       print('Before function execution')
       func()
       print('After function execution')
   return wrapper
@decorator
def hello_world():
   "Prints 'Hello, world!'"
   print('Hello, world!')
print(hello_world.__name__)
print(hello_world.__doc__)

这样,使用装饰器后的函数名和文档字符串能够保持不变。

Python 装饰器的应用实例

装饰器在实际的 Python 编程中有许多应用场景,比如日志记录、性能测试、事务处理、缓存、权限校验等。

一个常见的应用就是使用装饰器进行日志记录:

import logging

def log_decorator(func):
   logging.basicConfig(level=logging.INFO)

def wrapper(*args, **kwargs):
       logging.info(f'Running "{func.__name__}" with arguments {args} and kwargs {kwargs}')
       result = func(*args, **kwargs)
       logging.info(f'Finished "{func.__name__}" with result {result}')
       return result

return wrapper

@log_decorator
def add(x, y):
   return x + y

这个装饰器记录了函数的名称,函数调用的参数,以及函数返回的结果。

装饰器链

Python 允许我们将多个装饰器应用到一个函数上,形成一个装饰器链。例如,我们可以同时应用日志装饰器和性能测试装饰器:

import time
import logging
from functools import wraps

def log_decorator(func):
   logging.basicConfig(level=logging.INFO)

@wraps(func)
   def wrapper(*args, **kwargs):
       logging.info(f'Running "{func.__name__}" with arguments {args} and kwargs {kwargs}')
       result = func(*args, **kwargs)
       logging.info(f'Finished "{func.__name__}" with result {result}')
       return result

return wrapper

def timer_decorator(func):
   @wraps(func)
   def wrapper(*args, **kwargs):
       start_time = time.time()
       result = func(*args, **kwargs)
       end_time = time.time()
       print(f'Function "{func.__name__}" took {end_time - start_time} seconds to run.')
       return result

return wrapper

@log_decorator
@timer_decorator
def add(x, y):
   time.sleep(2)
   return x + y

在这个例子中,@log_decorator 和 @timer_decorator 两个装饰器被同时应用到 add 函数上,它们分别负责记录日志和测量函数运行时间。

One More Thing: 自动注册装饰器

一个有趣的装饰器应用是自动注册。这个装饰器会在装饰函数时自动将函数添加到一个列表或字典中,这样我们就可以在程序的其他地方访问到这个列表或字典,知道有哪些函数被装饰过。

# 装饰器将函数注册到一个列表中
def register_decorator(func_list):
   def decorator(func):
       func_list.append(func)
       return func
   return decorator

# 自动注册函数
registered_functions = []
@register_decorator(registered_functions)
def foo():
   pass

@register_decorator(registered_functions)
def bar():
   pass

print(registered_functions)  # 输出: [<function foo at 0x10d38d160>, <function bar at 0x10d38d1f0>]

这个装饰器可以用于自动注册路由、插件系统、命令行参数处理等场景,能够大大提高代码的灵活性和可扩展性。

来源:https://www.cnblogs.com/xfuture/p/17445659.html

标签:Python,装饰器
0
投稿

猜你喜欢

  • 一句Sql把纵向表转为横向表,并分别分组求平均和总平均值

    2024-01-22 19:30:37
  • JS如何生成一个不重复的ID的函数

    2024-04-17 09:48:51
  • Flask-SQLALchemy基本使用方法

    2023-07-01 23:19:16
  • Python实现平行坐标图的两种方法小结

    2023-07-30 20:45:34
  • Python关于print的操作(倒计时、转圈显示、进度条)

    2022-08-19 07:26:58
  • CSS兼容IE6,IE7,FF的技巧

    2010-04-01 12:34:00
  • MySQL中Decimal类型和Float Double的区别(详解)

    2024-01-26 16:37:55
  • MySQL创建用户与授权方法

    2024-01-19 02:43:55
  • 跟老齐学Python之关于循环的小伎俩

    2022-07-20 07:03:36
  • PHP后台实现微信小程序登录

    2024-03-14 22:17:25
  • 树莓派4B+opencv4+python 打开摄像头的实现方法

    2021-05-04 12:09:37
  • python 如何停止一个死循环的线程

    2021-04-17 04:25:34
  • 微信小程序仿今日头条导航栏滚动解析

    2024-04-29 13:55:42
  • Python之捕捉异常详解

    2022-06-10 02:27:30
  • OpenCV实现去除背景识别的方法总结

    2021-01-06 23:04:10
  • 使用pipenv管理python虚拟环境的全过程

    2021-08-26 13:05:55
  • python库pydantic的简易入门教程

    2022-06-27 14:05:28
  • django 扩展user用户字段inlines方式

    2022-02-28 00:50:14
  • 使用shell检查并修复mysql数据库表的脚本

    2024-01-27 23:52:35
  • Pytorch 实现自定义参数层的例子

    2023-01-27 22:00:06
  • asp之家 网络编程 m.aspxhome.com