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教程。