一篇文章带你了解Python中的装饰器
作者:宇宙之一粟 发布时间:2022-11-23 14:00:14
前言
本文将带你学习装饰器在 Python 中的工作原理,如果在函数和类中使用装饰器,如何利用装饰器避免代码重复(DRY 原则,Don’t Repeat Yourself )。
Python 中的装饰器是什么
装饰器在 Python中是一个非常强大和有用的工具,因为它允许程序员修改函数或类的行为。装饰器允许我们包装另一个函数,以扩展包装函数的行为,而无需修改基础函数定义。这也被称为元编程,因为程序本身在程序运行时会尝试修改自身的另一部分。
装饰器是语法糖: 在代码中利用更简洁流畅的语法实现更为复杂的功能。
我们知道,Python 一切皆对象。这意味着 Python 中的函数可以用作参数或作为参数传递。一等函数的属性:
函数是 Object 类型的实例。
可以将函数存储在变量中。
可以将该函数作为参数传递给另一个函数。
可以从函数中返回函数。
可以将它们存储在数据结构中,例如哈希表,列表等。
让我们看一个这样的例子。
def hello():
print('Welcome to Python Decorator!')
another_hello = hello()
another_hello
# Welcome to Python Decorator!
定义了一个 hello() 函数,然后将 hello 函数分配给 another_hello 变量,然后调用这个变量,得到的结果是 hello 函数被执行。
既然 Python 中的函数是对象,那么除了可以简单的调用之外,就可以把函数作为对象传递给另一个函数。
def print_welcome():
print('Welcome to Python Decorator!')
def print_hello(func):
def inner():
print('Hello!')
func()
return inner
decorated = print_hello(print_welcome)
decorated()
# Hello!
# Welcome to Python Decorator!
语法糖
但是,上面的代码使用了内部函数我们可以通过简单地用装饰器函数 print_hello() 来装饰 print_welcome() 函数。
装饰器可以简化我们的操作。功能完全一样,但它的代码更简洁。即通过 @ 符号简化装饰器的使用,如下所示:
def print_hello(func):
def inner():
print('Hello!')
func()
return inner
@print_hello
def print_welcome():
print('Welcome to Python Decorator!')
print_welcome()
# Hello!
# Welcome to Python Decorator!
通过这样做,我们能够消除将一个函数显式传递到另一个函数中的使用。Python 装饰器隐式处理这一点。
使用 Python 装饰器修改函数行为
使用 Python 装饰器对函数进行计时
为了演示它们的实用性,让我们构建一个函数,该函数采用另一个函数并对其执行进行计时。在这里,使用装饰器的好处是它允许我们遵循 DRY 编程原则。
装饰器可用于测量函数执行所需的时间。 如果你定义一个简单的睡眠函数,以计算该函数的运行时。
import time
def timeit(func):
def timed():
start = time.time()
result = func()
end = time.time()
print(f'Program took {(end - start) * 1000}s to run')
return result
return timed
@timeit
def print_welcome():
print('Welcome to Python Decorator!')
print_welcome()
# Welcome to Python Decorator!
# Program took 0.0s to run
分析一下上面的代码:
定义了一个函数
timeit()
接受另一个函数该函数还有另一个内部函数
timed()
函数跟踪开始时间,执行修饰函数,跟踪结束时间,计算差值并返回结果
最后,外层函数返回内层函数
当我们将此装饰器函数应用于我们的函数 print_welcome()
时,首先会返回欢迎问候语,然后显示执行时间。
使用 Python 装饰器将有用信息记录到终端
与上面的例子类似,我们可以在程序运行时使用装饰器将有用的信息打印到终端。例如,我们可能想知道正在运行哪个函数以及当前时间。也可以使用装饰器传递到日志文件:
from datetime import datetime
def log_info(func):
def inner():
print(f'Starting run at {datetime.now()}')
print(f'Running {func.__name__}')
func()
return inner
@log_info
def print_welcome():
print('Welcome to Python Decorator!')
print_welcome()
# Starting run at 2022-03-27 23:26:38.473310
# Running print_welcome
# Welcome to Python Decorator!
在上面的示例中,在运行函数之前,我们的装饰器打印当前日期和时间以及将要运行的函数的名称。如果您正在运行较长的脚本,并且只是想知道程序的位置,这可能很有用。
Web app 中使用的装饰器
让我们以 Web 应用程序的用例为例。当您在 Flask 中构建 Web 应用程序时,您总是会编写 url 路由。 每条路线都是 Web 应用程序中的特定页面。 打开页面 /about
可能会调用 about_page()
方法。
@app.route("/about")
def about_page():
return "Website about nachos"
将参数传递给 Python 装饰器
到目前为止,您已经学习了如何创建一些有用的 Python 装饰器。然而,这些装饰器都没有传入参数。在本节中,您将学习如何创建接受参数的 Python 装饰器。
为此,我们将允许在 Python 语法魔术解压缩。使用 func_name(*args,**kwargs)
,它将解压缩所有参数和所有关键字参数。通过在装饰器中使用它,可以确保装饰器将接受任意数量的参数或关键字参数。这使得它们在重复使用时更加实用。
def print_function_name(func):
def inner(*args, **kwargs):
print(f'Running {func.__name__}...')
return func(*args, **kwargs)
return inner
@print_function_name
def add_nums(a, b):
print(a + b)
add_nums(1, 2)
# Running add_nums...
# 3
上述方法的美妙之处在于它同时接受位置和关键字参数。因此,即使我们以以下任何格式执行该函数,该函数也将运行:
add_nums(1024, 2020)
add_nums(1024, b = 2021)
add_nums(a = 1024, b = 2222)
使用多个 Python 装饰器
关于 Python 装饰器的一个有趣的方式是:可以同时使用多个装饰器。这意味着您可以将多个装饰器应用于单个函数。为了理解这一点,来看一个例子:
def one(func):
def inner(*args, **kwargs):
print('1')
return func(*args, **kwargs)
return inner
def two(func):
def inner(*args, **kwargs):
print('2')
return func(*args, **kwargs)
return inner
@one
@two
def speak(text):
print(text)
speak('Hello')
# 1
# 2
# Hello
我们的装饰器函数所做的唯一事情就是打印出数字 1 和数字 2。通过将装饰器 @one
放在 @two
之前,您可以将 two()
包装的函数包装为 one()
。为了说明这一点,您可以切换顺序以查看如何修改行为:
# Changing decorator order
@two
@one
def speak(text):
print(text)
speak('Hello')
# 2
# 1
# Hello
通过首先放置 @two
装饰器,该函数成为最外层的函数。
Primer on Python Decorators – Real Python
Python Decorators Introduction - Python Tutorial (pythonbasics.org)
来源:https://juejin.cn/post/7080549589589164045
猜你喜欢
- 我想大多数的人在编写ASP程序的时候,都碰到过类似的错误信息: Error Num
- getattr函数(1)使用 getattr 函数,可以得到一个直到运行时才知道名称的函数的引用。>>> li = [&q
- 以上是开头,安装完后需要导入转载的代码读取所有docx文件中的内容发现没有读取到表格数据:from docx import Document
- 本文实例讲述了python连接字符串的方法。分享给大家供大家参考。具体如下:方法1:直接通过加号操作符相加foobar = 'foo
- 一、在CentOS上安装Python31.下载Python3.10源代码文件下载地址:https://www.python.org/down
- 像微博一类的平台上传图片时,平台都会添加一个水印,宣誓着对图片的所有权,我们自己的博客平台也可以给自己的图片添加上水印。还是用 Pillow
- 1-删除模型变量del model_define2-清空CUDA cachetorch.cuda.empty_cache()3-步骤2(异步
- Numpy中repeat函数使用Numpy是Python强大的数学计算库,和Scipy一起构建起Python科学计算生态。在本节下面我们重点
- 一、分析链接上一篇文章指路一般来说,我们参加某个网站的盖楼抽奖活动,并不是仅仅只参加一个,而是多个盖楼活动一起参加。这个时候,我们就需要分析
- 游戏截图动态演示源码分享state/tool.pyimport osimport jsonfrom abc import abstractm
- 最近在用python写接口的测试程序,期间用到解析字典获取某个key的value,由于多个接口返回的字典格式不是固定的并存在多层嵌套的情况。
- 今天帮一同学导入一个excel数据,我把excel保存为txt格式,然后建表导入,失败!分隔符格式不匹配,无法导入!!!!怎么看两边都是\t
- 在JAVASCRIPT中LEFT函数的等价函数:function left(mainStr,lngLen) {if&nb
- 在浏览器 IE6 、IE7、Firefox2+、Firefpx3+、Opera9.6+、Safari3.1+中测试以下代码:<!DOC
- 如何将123456789转化成123,456,789这样的形式呢?很多流量大的站比如优酷都有这样的格式。也是设计程序最常用的算
- 本文实例讲述了python求解水仙花数的方法。分享给大家供大家参考。具体如下:一个N位的十进制正整数,如果它的每个位上的数字的N次方的和等于
- <%DIM LinkArray(4,2)' 定义链接数组LinkArray(0,1)=&quo
- type 所有类是type生成的a = 1b = "abc"print("type a:{}&qu
- 一、求解方法、算法和编程方案线性规划 (Linear Programming,LP) 是很多数模培训讲的第一个算法,算法很简单,思想很深刻。
- json_encode()如何转化一个对象? 使用 json_encode() 将数组 array