?
Python 装饰器允许你在不修改函数本身的情况下更改函数的行为。
在本文中,我将向你展示如何创建和使用装饰器。你将看到使用这个高级的 Python 功能是多么容易。
在本文中,我将讨论以下主题:
- 何时在 Python 中使用装饰器
- 用于创建装饰器的构建块
- 如何创建一个 Python 装饰器
- Python 装饰器的真实示例
- Python 中的类装饰器
何时在 Python 中使用装饰器
当你需要更改函数的行为而不修改函数本身时,你将使用装饰器,比如当你想要添加日志记录、测试性能、执行缓存、验证权限等时。
当你需要在多个函数上运行相同的代码时,也可以使用装饰器。这可以避免你编写重复的代码。
以下是用于创建 Python 装饰器的构建块
为了更好地理解装饰器的工作原理,你应该先了解一些概念。
- 函数是一个对象。因此,可以将函数分配给变量,可以从该变量访问该函数。
def my_function():
print('I am a function.')
# Assign the function to a variable without parenthesis. We don't want to execute the function.
description = my_function
# Accessing the function from the variable I assigned it to.
print(description())
# Output
'I am a function.'
2. 一个函数可以嵌套在另一个函数中。
def outer_function():
def inner_function():
print('I came from the inner function.')
# Executing the inner function inside the outer function.
inner_function()
outer_function()
# Output
I came from the inner function.
请注意,inner_function ?在?outer_function ?之外不可用。如果我尝试在?outer_function ?之外执行?inner_function ,则会收到 NameError 异常。
inner_function()
Traceback (most recent call last):
File "/tmp/my_script.py", line 9, in <module>
inner_function()
NameError: name 'inner_function' is not defined
3. 由于一个函数可以嵌套在另一个函数中,所以它也可以被返回。
def outer_function():
'''Assign task to student'''
task = 'Read Python book chapter 3.'
def inner_function():
print(task)
return inner_function
homework = outer_function()
homework()
# Output
'Read Python book chapter 5.'
4. 一个函数可以作为参数传递给另一个函数。
def friendly_reminder(func):
'''Reminder for husband'''
func()
print('Don\'t forget to bring your wallet!')
def action():
print('I am going to the store buy you something nice.')
# Calling the friendly_reminder function with the action function used as an argument.
friendly_reminder(action)
# Output
I am going to the store buy you something nice.
Don't forget to bring your wallet!
如何创建一个 Python 装饰器
为了在 Python 中创建装饰器函数,我创建了一个将函数作为参数的外部函数,还有一个内部函数包含装饰函数。
以下是基本的 Python 装饰器的语法:
def my_decorator_func(func):
def wrapper_func():
# Do something before the function.
func()
# Do something after the function.
return wrapper_func
要使用装饰器,你可以将其附加到一个函数,就像你在下面的代码中看到的那样。我们通过将装饰器的名称直接放在我们想要使用它的函数上方来使用装饰器。使用?@ ?符号作为装饰器函数的前缀。
@my_decorator_func
def my_func():
pass
这是一个简单的例子。此装饰器记录执行函数的日期和时间:
from datetime import datetime
def log_datetime(func):
'''Log the date and time of a function'''
def wrapper():
print(f'Function: {func.__name__}\nRun on: {datetime.today().strftime("%Y-%m-%d %H:%M:%S")}')
print(f'{"-"*30}')
func()
return wrapper
@log_datetime
def daily_backup():
print('Daily backup job has finished.')
daily_backup()
# Output
Daily backup job has finished.
Function: daily_backup
Run on: 2021-06-06 06:54:14
---------------------------
如何在 Python 中为装饰器添加参数
可以将参数传递给装饰器。为了给装饰器添加参数,我将?*args ?和?* *kwargs ?添加到内部函数中。
* args ?将采用任意类型的无限数量的参数,例如?10 、True ?或?'Brandon' 。** kwargs ?将采用无限数量的关键字参数,例如?count=99 、is_authenticated=True ?或?name='Brandon' 。
这是一个带参数的装饰器:
def my_decorator_func(func):
def wrapper_func(*args, **kwargs):
# Do something before the function.
func(*args, **kwargs)
# Do something after the function.
return wrapper_func
@my_decorator_func
def my_func(my_arg):
'''Example docstring for function'''
pass
装饰器隐藏正在装饰的函数。如果我检查?__name__ ?或?__doc__ ?方法,我们会得到一个意想不到的结果。
print(my_func.__name__)
print(my_func.__doc__)
# Output
wrapper_func
None
为了解决这个问题,我将使用?functools ,它将使用装饰函数属性更新装饰器。
from functools import wraps
def my_decorator_func(func):
@wraps(func)
def wrapper_func(*args, **kwargs):
func(*args, **kwargs)
return wrapper_func
@my_decorator_func
def my_func(my_args):
'''Example docstring for function'''
pass
现在我得到了我期待的输出。
print(my_func.__name__)
print(my_func.__doc__)
# Output
my_func
Example docstring for function
运行中的 Python 装饰器示例
我创建了一个装饰器来测量函数的内存和速度。我们将使用装饰器来测试性能列表生成,使用四种方法:范围、列表、追加和连接。
from functools import wraps
import tracemalloc
from time import perf_counter
def measure_performance(func):
'''Measure performance of a function'''
@wraps(func)
def wrapper(*args, **kwargs):
tracemalloc.start()
start_time = perf_counter()
func(*args, **kwargs)
current, peak = tracemalloc.get_traced_memory()
finish_time = perf_counter()
print(f'Function: {func.__name__}')
print(f'Method: {func.__doc__}')
print(f'Memory usage:\t\t {current / 10**6:.6f} MB \n'
f'Peak memory usage:\t {peak / 10**6:.6f} MB ')
print(f'Time elapsed is seconds: {finish_time - start_time:.6f}')
print(f'{"-"*40}')
tracemalloc.stop()
return wrapper
@measure_performance
def make_list1():
'''Range'''
my_list = list(range(100000))
@measure_performance
def make_list2():
'''List comprehension'''
my_list = [l for l in range(100000)]
@measure_performance
def make_list3():
'''Append'''
my_list = []
for item in range(100000):
my_list.append(item)
@measure_performance
def make_list4():
'''Concatenation'''
my_list = []
for item in range(100000):
my_list = my_list + [item]
make_list1()
make_list2()
make_list3()
make_list4()
# Output
Function: make_list1
Method: Range
Memory usage: 0.000072 MB
Peak memory usage: 3.693040 MB
Time elapsed is seconds: 0.049359
----------------------------------------
Function: make_list2
Method: List comprehension
Memory usage: 0.000856 MB
Peak memory usage: 3.618244 MB
Time elapsed is seconds: 0.052338
----------------------------------------
Function: make_list3
Method: Append
Memory usage: 0.000448 MB
Peak memory usage: 3.617692 MB
Time elapsed is seconds: 0.060719
----------------------------------------
Function: make_list4
Method: Concatenation
Memory usage: 0.000440 MB
Peak memory usage: 4.393292 MB
Time elapsed is seconds: 61.649138
----------------------------------------
你也可以将装饰器与类一起使用。让我们看看如何在 Python 类中使用装饰器。
在此示例中,请注意不涉及?@ ?字符。使用?__call__ ?方法,装饰器在创建类的实例时执行。
这个类跟踪查询 API 的函数已运行的次数。一旦达到限制,装饰器就会停止执行该函数。
import requests
class LimitQuery:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.limit = args[0]
if self.count < self.limit:
self.count += 1
return self.func(*args, **kwargs)
else:
print(f'No queries left. All {self.count} used.')
return
@LimitQuery
import requests
def get_coin_price():
'''View the Bitcoin Price Index (BPI)'''
url = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json')
if url.status_code == 200:
text = url.json()
return f"${float(text['bpi']['USD']['rate_float']):.2f}")
get_coin_price(5)
get_coin_price(5)
get_coin_price(5)
get_coin_price(5)
get_coin_price(5)
# Output
$35968.25
$35896.55
$35368.14
$35962.27
No queries left. All 5 used.
这个类将跟踪类的状态。
结语
如果对Python有兴趣,想了解更多的Python以及AIoT知识,解决测试问题,以及入门指导,帮你解决学习Python中遇到的困惑,我们这里有技术高手。如果你正在找工作或者刚刚学校出来,又或者已经工作但是经常觉得难点很多,觉得自己Python方面学的不够精想要继续学习的,想转行怕学不会的, 都可以加入我们,可领取最新Python大厂面试资料和Python爬虫、人工智能、学习资料!VX【pydby01】暗号CSDN
?
在这篇文章中,我谈到了如何将函数传递给变量、嵌套函数、返回函数以及将函数作为参数传递给另一个函数。
我还向你展示了如何创建和使用 Python 装饰器以及一些实际示例。希望你现在能够在你的项目中添加装饰器了。
|