Python装饰器教程展示了如何在Python中使用装饰器函数。
Python函数是一等公民。这意味着函数与Python中的其他对象具有同等地位。函数可以分配给变量、存储在集合中、动态创建和删除,或者作为参数传递。
嵌套函数,也称为内部函数,是在另一个函数中定义的函数。
Python装饰器
Python装饰器在不修改可调用对象本身的情况下扩展和修改可调用对象的行为。装饰器是装饰(或包装)其他函数并在包装函数运行前后执行代码的函数。
Python装饰器通常用于日志记录、身份验证和授权、计时和缓存。
Python装饰器示例
在下一个示例中,我们创建一个简单的装饰器示例。
#!/usr/bin/python
def enclose(fun):
def wrapper():
print("***************************")
fun()
print("***************************")
return wrapper
def myfun():
print("myfun")
enc = enclose(myfun)
enc()
enclose函数是一个装饰器,它通过在其输出中添加星号来扩展装饰函数。
def enclose(fun): ...
enclose函数接受一个函数作为参数。
def wrapper():
print("***************************")
fun()
print("***************************")
return wrapper
wrapper用星星装饰传递的函数。返回包装函数。
def myfun():
print("myfun")
这是一个需要修饰的常规函数。
enc = enclose(myfun) enc()
myfun被传递给enclose函数,它在其中被扩展。返回并调用包装函数。
$ ./enclose.py *************************** myfun ***************************
这是输出。装饰器在常规函数的输出前后添加星星。
带有@符号的Python装饰器
Python允许使用@符号来标记要用装饰器装饰的方法。
#!/usr/bin/python
def enclose(fun):
def wrapper():
print("***************************")
fun()
print("***************************")
return wrapper
@enclose
def myfun():
print("myfun")
myfun()
在功能上,该示例与上一个示例等效。只是使用了不同的语法。
用参数修饰函数
以下示例展示了如何修饰带参数的函数。
#!/usr/bin/python
def enclose(fun):
def wrapper(val):
print("***************************")
fun(val)
print("***************************")
return wrapper
@enclose
def myfun(val):
print(f"myfun with {val}")
myfun('falcon')
在此代码示例中,常规函数采用一个参数。
#!/usr/bin/python
def enclose(fun):
def wrapper(*args, **kwargs):
print("***************************")
fun(*args, **kwargs)
print("***************************")
return wrapper
@enclose
def myfun(name, age):
print(f'{name} is {age} years old')
myfun(name='Peter', age=32)
myfun('Roman', 29)
此示例展示了如何使用*args,**kwargs语法处理可变数量的参数。
Python装饰器修改数据
装饰函数可以修改被装饰函数的数据。
#!/usr/bin/python
def uppercase(fun):
def wrapper():
res = fun()
modified = res.upper()
return modified
return wrapper
@uppercase
def gen_message():
return 'Hello there!'
msg = gen_message()
print(msg)
@uppercase装饰器将返回的文本更改为大写。
def uppercase(fun):
def wrapper():
res = fun()
modified = res.upper()
return modified
return wrapper
在包装函数中,文本被修改并返回。
$ ./uppercase.py HELLO THERE!
Python多重堆叠装饰器
可以在一个函数上应用多个装饰器。
#!/usr/bin/python
def strong(fun):
def wrapper():
return f'<strong>{fun()}</strong>'
return wrapper
def em(fun):
def wrapper():
return f'<em>{fun()}</em>'
return wrapper
@strong
@em
def message():
return 'This is some message'
print(message())
在示例中,我们在文本上应用了两个HTML标记。
$ ./multiple_decors.py <strong><em>This is some message</em></strong>
Python装饰器计时示例
在下面的例子中,我们在一个函数上应用了一个定时器装饰器。
#!/usr/bin/python
import time
import math
def timer(func):
def wrapper(*args, **kwargs):
begin = time.time()
f = func(*args, **kwargs)
end = time.time()
print("Total time taken in : ", func.__name__, end - begin)
return f
return wrapper
@timer
def factorial(num):
return math.factorial(num)
f = factorial(4580)
print(f)
该示例使用装饰器计算factorial函数运行的时间。
begin = time.time()
在函数运行之前,我们获取开始时间。
end = time.time()
print("Total time taken in : ", func.__name__, end - begin)
函数运行后,我们得到结束时间并打印差值。
functools@wraps装饰器
应用装饰器函数后,原函数的__name__、__doc__、__module__属性都丢失了。这使得调试变得尴尬。要解决此问题,我们可以使用functool的@wraps装饰器。
#!/usr/bin/python
from functools import wraps
def enclose(fun):
@wraps(fun)
def wrapper():
'''This is wrapper function'''
print("***************************")
fun()
print("***************************")
return wrapper
@enclose
def myfun():
'''this is myfun()'''
print("myfun")
myfun()
print(myfun.__name__)
print(myfun.__doc__)
在示例中,我们在包装函数上应用了@wraps装饰器。保留原始函数(myfun)的名称和文档字符串。
$ ./naming.py *************************** myfun *************************** myfun this is myfun()
Python类装饰器
可以使用类作为装饰器。为此,我们需要实现__call__魔法函数。
#!/usr/bin/python
import functools
class CountCalls:
def __init__(self, fun):
functools.update_wrapper(self, fun)
self.fun = fun
self.num_of_calls = 0
def __call__(self, *args, **kwargs):
self.num_of_calls += 1
print(f"Call {self.num_of_calls} of {self.fun.__name__} fun")
return self.fun(*args, **kwargs)
@CountCalls
def hello():
print("Hello there!")
hello()
hello()
hello()
在示例中,我们使用类装饰器来计算常规函数的调用。
def __init__(self, fun):
functools.update_wrapper(self, fun)
self.fun = fun
self.num_of_calls = 0
我们调用update_wrapper函数。它与@wraps装饰器具有相同的目的;即它保留原始函数的元数据(__name__或__doc__)。我们保留对原始函数的引用并设置num_of_calls变量。
def __call__(self, *args, **kwargs):
self.num_of_calls += 1
print(f"Call {self.num_of_calls} of {self.fun.__name__} fun")
return self.fun(*args, **kwargs)
我们增加num_of_calls变量,打印一条消息,并调用原始函数,向其传递可能的参数。
$ ./counting_calls.py Call 1 of hello fun Hello there! Call 2 of hello fun Hello there! Call 3 of hello fun Hello there!
Python@staticmethod装饰器
Python有@staticmethod内置装饰器,它在Python类中创建一个静态方法。静态方法属于类,无需创建实例即可调用。
#!/usr/bin/python
class Math:
@staticmethod
def abs(x):
if x < 0:
return -x
return x
print(Math.abs(3))
print(Math.abs(-3))
在示例中,我们使用@staticmethod装饰器创建静态abs方法。通过指定类名并使用点运算符调用该方法:Math.abs。
烧瓶装饰器
流行的Python框架Flask使用装饰器。例如,@app.route用于定义路由。
#!/usr/bin/python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello there!'
在示例中,hello函数使用Flask的@app.route装饰器映射到根页面。
在本教程中,我们使用了Python装饰器。
列出所有Python教程。
