51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 921|回复: 0
打印 上一主题 下一主题

[转贴] 一文了解 Python 中的装饰器

[复制链接]
  • TA的每日心情
    无聊
    前天 09:05
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-4-6 10:19:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    前言
      本文将带你学习装饰器在 Python 中的工作原理,如果在函数和类中使用装饰器,如何利用装饰器避免代码重复(DRY 原则,Don’t Repeat Yourself )。
      Python 中的装饰器是什么
      装饰器在 Python中是一个非常强大和有用的工具,因为它允许程序员修改函数或类的行为。装饰器允许我们包装另一个函数,以扩展包装函数的行为,而无需修改基础函数定义。这也被称为元编程,因为程序本身在程序运行时会尝试修改自身的另一部分。
      装饰器是?语法糖: ?在代码中利用更简洁流畅的语法实现更为复杂的功能。
      我们知道,Python 一切皆对象。这意味着 Python 中的函数可以用作参数或作为参数传递。一等函数的属性:
      ·函数是 Object 类型的实例。
      · 可以将函数存储在变量中。
      · 可以将该函数作为参数传递给另一个函数。
      · 可以从函数中返回函数。
      · 可以将它们存储在数据结构中,例如哈希表,列表等。
      让我们看一个这样的例子。
    1.  def hello():
    2.       print('Welcome to Python Decorator!')
    3.      
    4.   another_hello = hello()
    5.   another_hello
    6.   # Welcome to Python Decorator!
    复制代码
     定义了一个 ??hello()?? 函数,然后将 hello 函数分配给 another_hello 变量,然后调用这个变量,得到的结果是 hello 函数被执行。
      既然 Python 中的函数是对象,那么除了可以简单的调用之外,就可以把函数作为对象传递给另一个函数。
    1. def print_welcome():
    2.       print('Welcome to Python Decorator!')
    3.      
    4.   def print_hello(func):
    5.       def inner():
    6.           print('Hello!')
    7.           func()
    8.       return inner
    9.   decorated = print_hello(print_welcome)
    10.   decorated()
    11.   # Hello!
    12.   # Welcome to Python Decorator!
    复制代码
     语法糖
      但是,上面的代码使用了内部函数我们可以通过简单地用装饰器函数 ??print_hello()??? 来装饰 ??print_welcome()?? 函数。
      装饰器可以简化我们的操作。功能完全一样,但它的代码更简洁。即通过 ??@?? 符号简化装饰器的使用,如下所示:
    1. def print_hello(func):
    2.       def inner():
    3.           print('Hello!')
    4.           func()
    5.       return inner
    6.   @print_hello
    7.   def print_welcome():
    8.       print('Welcome to Python Decorator!')
    9.      
    10.   print_welcome()
    11.   # Hello!
    12.   # Welcome to Python Decorator!
    复制代码
    通过这样做,我们能够消除将一个函数显式传递到另一个函数中的使用。Python 装饰器隐式处理这一点。
      使用 Python 装饰器修改函数行为
      使用 Python 装饰器对函数进行计时
      为了演示它们的实用性,让我们构建一个函数,该函数采用另一个函数并对其执行进行计时。在这里,使用装饰器的好处是它允许我们遵循 DRY 编程原则。
      装饰器可用于测量函数执行所需的时间。 如果你定义一个简单的睡眠函数,以计算该函数的运行时。

    1.  import time
    2.   def timeit(func):
    3.       def timed():
    4.           start = time.time()
    5.           result = func()
    6.           end = time.time()
    7.           print(f'Program took {(end - start) * 1000}s to run')
    8.           return result
    9.       return timed
    10.   @timeit
    11.   def print_welcome():
    12.       print('Welcome to Python Decorator!')
    13.      
    14.   print_welcome()
    15.   # Welcome to Python Decorator!
    16.   # Program took 0.0s to run
    复制代码
    分析一下上面的代码:
      ·定义了一个函数 ??timeit()?? 接受另一个函数
      · 该函数还有另一个内部函数 ??timed()??
      · 函数跟踪开始时间,执行修饰函数,跟踪结束时间,计算差值并返回结果
      · 最后,外层函数返回内层函数
      当我们将此装饰器函数应用于我们的函数 ??print_welcome()?? 时,首先会返回欢迎问候语,然后显示执行时间。
      使用 Python 装饰器将有用信息记录到终端
      与上面的例子类似,我们可以在程序运行时使用装饰器将有用的信息打印到终端。例如,我们可能想知道正在运行哪个函数以及当前时间。也可以使用装饰器传递到日志文件:
    1.  from datetime import datetime
    2.   def log_info(func):
    3.       def inner():
    4.           print(f'Starting run at {datetime.now()}')
    5.           print(f'Running {func.__name__}')
    6.           func()
    7.       return inner
    8.   @log_info
    9.   def print_welcome():
    10.       print('Welcome to Python Decorator!')
    11.      
    12.   print_welcome()
    13.   # Starting run at 2022-03-27 23:26:38.473310
    14.   # Running print_welcome
    15.   # Welcome to Python Decorator!
    复制代码
     在上面的示例中,在运行函数之前,我们的装饰器打印当前日期和时间以及将要运行的函数的名称。如果您正在运行较长的脚本,并且只是想知道程序的位置,这可能很有用。
      Web app 中使用的装饰器
      让我们以 Web 应用程序的用例为例。当您在 Flask 中构建 Web 应用程序时,您总是会编写 url 路由。 每条路线都是 Web 应用程序中的特定页面。 打开页面  /about  可能会调用  about_page()  方法。

    1. @app.route("/about")
    2.   def about_page():
    3.     return "Website about nachos"
    复制代码
    将参数传递给 Python 装饰器
      到目前为止,您已经学习了如何创建一些有用的 Python 装饰器。然而,这些装饰器都没有传入参数。在本节中,您将学习如何创建接受参数的 Python 装饰器。
      为此,我们将允许在 Python 语法魔术解压缩。使用 ??func_name(*args,**kwargs)??,它将解压缩所有参数和所有关键字参数。通过在装饰器中使用它,可以确保装饰器将接受任意数量的参数或关键字参数。这使得它们在重复使用时更加实用。
    1.  def print_function_name(func):
    2.       def inner(*args, **kwargs):
    3.           print(f'Running {func.__name__}...')
    4.           return func(*args, **kwargs)
    5.       return inner
    6.   @print_function_name
    7.   def add_nums(a, b):
    8.       print(a + b)
    9.   add_nums(1, 2)
    10.   # Running add_nums...
    11.   # 3
    复制代码
    上述方法的美妙之处在于它同时接受位置和关键字参数。因此,即使我们以以下任何格式执行该函数,该函数也将运行:
      ·add_nums(1024, 2020)??
      · add_nums(1024, b = 2021)??
      · add_nums(a = 1024, b = 2222)??
      使用多个 Python 装饰器
      关于 Python 装饰器的一个有趣的方式是:可以同时使用多个装饰器。这意味着您可以将多个装饰器应用于单个函数。为了理解这一点,来看一个例子:
    1. def one(func):
    2.       def inner(*args, **kwargs):
    3.           print('1')
    4.           return func(*args, **kwargs)
    5.       return inner
    6.      
    7.   def two(func):
    8.       def inner(*args, **kwargs):
    9.           print('2')
    10.           return func(*args, **kwargs)
    11.       return inner
    12.   @one
    13.   @two
    14.   def speak(text):
    15.       print(text)
    16.      
    17.   speak('Hello')
    18.   # 1
    19.   # 2
    20.   # Hello
    复制代码
     我们的装饰器函数所做的唯一事情就是打印出数字 1 和数字 2。通过将装饰器 @one  放在  @two  之前,您可以将  two() 包装的函数包装为  one() 。为了说明这一点,您可以切换顺序以查看如何修改行为:
    1.  # Changing decorator order
    2.   @two
    3.   @one
    4.   def speak(text):
    5.       print(text)
    6.      
    7.   speak('Hello')
    8.   # 2
    9.   # 1
    10.   # Hello
    复制代码
    通过首先放置 ??@two?? 装饰器,该函数成为最外层的函数。
      总结
      在本文中,我们先了解了什么是 Python 装饰器,它代表元编程的语法糖。 Python 装饰器允许我们修改函数行为并允许我们以不同的方式扩展函数。
      接着了解装饰器是什么以及如何使用它们。学习了如何允许将参数传递给 Python 装饰器,学习了几个常见的装饰器有用的工具。最后,如何使用多个装饰器以及装饰器的先后顺序。









    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-23 11:56 , Processed in 0.067627 second(s), 25 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

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