目录
装饰器
装饰器(Decorators)是Python的⼀个重要部分。
装饰器是修改其他函数的功能的函数。
- 有助于让我们的代码更简短;
- 有助于让我们的代码更Pythonic;
一切皆对象
# 定义函数
def hi(name="鲁班7号"):
string = "你好," + name + "!"
return string
print(hi())
返回的结果是如下。
你好,鲁班7号!
Process finished with exit code 0
在Python中可以将一个函数赋给一个变量,如下所示。
hello = hi
print(hello())
你好,鲁班7号!
Process finished with exit code 0
- 变量=函数名,可以用这个变量加上括号,使它具有函数功能;
- 变量=函数名,相当于“复制”原来的函数的内容给现在的变量,形成新的“函数”。当删除旧函数时,变量无影响。
- 变量=函数名+括号,变量实际就是函数的返回值;
# 把原来的函数删去
# 当删除旧函数时,变量无影响。
del hi
print(hello())
# 已删去的函数无法再调用
print(hi())
你好,鲁班7号!
Traceback (most recent call last):
File "C:/Users/aw/PycharmProjects/AdvancedPython/Decoration.py", line 17, in <module>
print(hi())
NameError: name 'hi' is not defined
Process finished with exit code 1
在函数中定义函数
我们可以创建嵌套的函数。
def Function1():
print("This is Function1's scope of execution")
def Function1_1():
print("This is Function1_1's scope of execution")
def Function1_1_1():
print("This is Function1_1_1's scope of execution")
def Function1_1_2():
print("This is Function1_1_2's scope of execution")
Function1_1_1()
Function1_1_2()
def Function1_2():
print("This is Function1_2's scope of execution")
print("This is Function1's scope of execution")
Function1_1()
Function1_2()
Function1()
返回的结果是如下。
This is Function1's scope of execution
This is Function1's scope of execution
This is Function1_1's scope of execution
This is Function1_1_1's scope of execution
This is Function1_1_2's scope of execution
This is Function1_2's scope of execution
但是无法调用函数里面的函数。
Function1_1_1()
Traceback (most recent call last):
File "C:/Users/aw/PycharmProjects/AdvancedPython/Decoration.py", line 45, in <module>
Function1_1_1()
NameError: name 'Function1_1_1' is not defined
在函数中返回函数
def Hi(name="鲁班7号"):
def FuncA():
return "出电刀。"
def FuncB():
return "出影刃。"
if name == "鲁班7号":
res = FuncA
else:
res = FuncB
return res
# 把“主函数”赋给变量,print的是赋给变量的函数在内存中的地址
a = Hi
print(a)
# 把“主函数”的返回值赋给变量,print的确实是返回值
# 但是这个返回值还是一个函数,因此返回的是这个函数的地址
a = Hi()
print(a)
# 把“主函数”的返回值函数的结果赋给变量,print的确实是返回值
# 这个返回值是一个文本
a = Hi()()
print(a)
# 修改“主函数”里面的传入参数值,可以得到不一样的效果
a = Hi(name="伽罗")()
print(a)
返回的结果如下。
<function Hi at 0x000001DC020FEDC0>
<function Hi.<locals>.FuncA at 0x000001DC020FED30>
出电刀。
出影刃。
Process finished with exit code 0
将函数作为参数传给另⼀个函数
# 将函数作为参数传递给另一个函数
def Func1():
return "攻速鞋"
def Func2(Func1):
print("我在玩鲁班七号")
print(Func1())
Func2(Func1)
返回的结果如下。
我在玩鲁班七号
攻速鞋
Process finished with exit code 0
第一个装饰器
# 第一个装饰器
def DecoratingFunction():
print("可爱的鲁班七号!")
def Decorator(DecoratingFunction):
def WrapTheFunction():
print("正在包装函数!")
DecoratingFunction()
print("包装函数结束!")
return WrapTheFunction
DecoratingFunction = Decorator(DecoratingFunction)
DecoratingFunction()
输出的结果是:
正在包装函数!
可爱的鲁班七号!
包装函数结束!
Process finished with exit code 0
最后是采用@的方式进行书写。
@Decorator
# 第一个装饰器
def DecoratingFunction():
print("可爱的鲁班七号!")
DecoratingFunction()
输出的结果也是一样的。
正在包装函数!
可爱的鲁班七号!
包装函数结束!
Process finished with exit code 0
注意
print(DecoratingFunction.__name__)
WrapTheFunction
Process finished with exit code 0
这是因为装饰器重写了我们函数的名字和注释⽂档(docstring)。
解决方法,使用functools包引进wraps。
@wraps(函数名)接受⼀个函数来进⾏装饰,并加⼊了复制函数名称、注释⽂档、参数列表等等的功能。这可以让我们在装饰器⾥⾯访问在装饰之前的函数的属性。
from functools import wraps
def Decorator(DecoratingFunction):
@wraps(DecoratingFunction)
def WrapTheFunction():
print("正在包装函数!")
DecoratingFunction()
print("包装函数结束!")
return WrapTheFunction
@Decorator
# 第一个装饰器
def DecoratingFunction():
print("可爱的鲁班七号!")
DecoratingFunction()
print(DecoratingFunction.__name__)
输出的结果是一样的了。
正在包装函数!
可爱的鲁班七号!
包装函数结束!
DecoratingFunction
装饰器的规范模板
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
# 一个函数只返回一个return,因此can_run==False的时候,第二个return不会返回
return decorated
@decorator_name
def func():
return("Function is running")
装饰器的使用场景
授权(Authorization)
装饰器能有助于检查某个⼈是否被授权去使⽤⼀个web应⽤的端点(endpoint)。
它们被⼤量使⽤于Flask和Django web框架中。
from functools import wraps
def RequiresAuthorization(Endpoint):
@wraps(Endpoint)
def decorating(*args, **kwargs):
auth_state = request.authorization
if not auth_state or not check_auth(auth_state.name, auth_state.password):
authenticate()
return Endpoint(*args, **kwargs)
return decorating
日志(Logging)
from functools import wraps
def logging(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called.")
return func(*args, **kwargs)
return with_logging
@logging
def add_math_function(x):
return x + x
print(add_math_function(2))
输出的结果如下。
add_math_function was called.
4
Process finished with exit code 0
日志延申——在函数中嵌入装饰器
from functools import wraps
import time
def logging(func, logfile='FunctionOutput.log'):
# FunctionOutput.log是待会要写进日志数据的文件
@wraps(func)
def with_logging(*args, **kwargs):
logfile_string = func.__name__ + " was called." + str(time.time()) + '\n'
print(logfile_string)
# 打开日志文件,生成句柄,进行日志书写
with open(logfile, 'a') as opened_file:
opened_file.write(logfile_string)
# 写完后进行被装饰函数的功能执行
return func(*args, **kwargs)
return with_logging
@logging
def add_math_function(x):
return x + x
print(add_math_function(2))
返回的结果是:
add_math_function was called.1656740346.1487005
4
Process finished with exit code 0
在PyCharm编译器下面看到生成了日志文件FunctionOutput.log
重复运行三次程序,可以看到,程序执行的函数名和执行时间均被记录了。
add_math_function was called.1656740285.4093149
add_math_function was called.1656740332.3984892
add_math_function was called.1656740346.1487005
装饰器类
类也可以⽤来构建装饰器。
以类的形式重新构建上述日志生成程序。
场景:你想把引起你注意的问题发送到⼀个email,同时也保留⽇志,留个记录。
from functools import wraps
import time
class logging(object):
def __init__(self, logfile="FunctionOutput.log"):
"""
初始化日志文件名。
:param logfile: 日志文件名。
"""
self.logfile = logfile
def __call__(self, func, *args, **kwargs):
@wraps(func)
def with_logging(*args, **kwargs):
logfile_string = func.__name__ + " was called. " + str(time.time()) + '\n'
print(logfile_string)
with open(self.logfile, 'a') as opened_file:
opened_file.write(logfile_string)
self.send_email()
return func(*args, **kwargs)
return with_logging
def send_email(self):
print("Sending Email ing...")
pass
# 对装饰器类进行实例化操作
logging = logging()
@logging
def add_math_function(x):
return x + x
print(add_math_function(2))
输出的结果是:
add_math_function was called. 1656741691.0767632
Sending Email ing...
4
Process finished with exit code 0
打开日志文件,程序运行了两次,可看到。
add_math_function was called. 1656741617.9853013
add_math_function was called. 1656741691.0767632