Python 中的闭包是一种高级特性,它可以让我们更加灵活地使用函数。
步骤思路
代码示例:
# 1.定义外部函数。
def outer(x):
# 2.定义内部函数。(内部函数使用到外部函数的变量。)
def inner(y):
print(f"outer_x={x} ,inner_y={y}")
return x + y
# 3.外部函数返回内部函数。
return inner
if __name__ == '__main__':
# 创建闭包实例。
o = outer(5)
# 调用闭包。
print(o(3))
# outer_x=5 ,inner_y=3
# 8
装饰器本身就是一个闭包,它可以保留被装饰函数的状态信息,并在被装饰函数执行前后添加额外的功能。
def check(fn):
def inner():
# 添加一个登录验证的功能。
print("请先登录....")
# 调用原函数。
fn()
return inner
def comment():
print("发表评论")
if __name__ == '__main__':
# 使用装饰器来装饰函数。
comment = check(comment)
comment()
# 请先登录....
# 发表评论
@装饰器名字
,通过语法糖的方式也可以完成对已有函数的装饰。# 定义一个装饰器。
def browser(fn):
# 内部函数:实现需要添加的新功能。
def open():
print("打开浏览器...")
# 调用原函数。
fn()
print("关闭浏览器!")
# 返回内部函数。
return open
@browser
# @browser为语法糖等同于:login = browser(login)
def login():
print("登录...")
if __name__ == '__main__':
login()
# 打开浏览器...
# 登录...
# 关闭浏览器!
def logger(fn):
"""
装饰带有参数的函数。
:param fn:
:return:
"""
def inner(a, b):
print("--- 开始计算 ---")
fn(a, b)
return inner
@logger
def my_sum(a, b):
print(a + b)
if __name__ == '__main__':
my_sum(3, 6)
# --- 开始计算 ---
# 9
None
。def logger(fn):
"""
装饰带有返回值的函数。
:param fn:
:return:
"""
def inner(a, b):
print("--- 开始计算 ---")
result = fn(a, b)
return result
return inner
@logger
def my_sum(a, b):
return a + b
if __name__ == '__main__':
print(my_sum(3, 6))
# --- 开始计算 ---
# 9
def decorate(fn):
"""
接受任意参数的装饰器。
:param fn:
:return:
"""
def inner(*args, **kwargs):
# TODO
result = fn(*args, **kwargs)
return result
return inner
带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式:
@装饰器(参数,...)
def logger(flag):
"""
带有参数的装饰器。
:param flag:
:return:
"""
def decorator(fn):
def inner(a, b):
# 根据装饰器参数,有不同的日志提示功能。
if flag == "+":
print("--- 开始加法计算 ---")
elif flag == "-":
print("--- 开始减法计算 ---")
else:
raise Exception("参数不合法!")
return fn(a, b)
return inner
return decorator
@logger("+")
def my_sum(a, b):
return a + b
@logger("-")
def my_sub(a, b):
return a - b
if __name__ == '__main__':
print(my_sum(3, 6))
# --- 开始加法计算 ---
# 9
print(my_sub(6, 3))
# --- 开始减法计算 ---
# 3
装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。
__call__()
方法来实现。@
语法应用装饰器时,Python 会调用装饰器类的 __init__()
方法创建一个实例,然后将被装饰的函数或类作为参数传递给 __init__()
方法。__call__()
方法。class Logger(object):
def __init__(self, fn):
# 将被装饰的函数 fn 保存在实例属性 fn 中。
self.__fn = fn
def __call__(self, *args, **kwargs):
"""
实现__call__方法,表示把类像调用函数一样进行调用。
:param args:
:param kwargs:
:return:
"""
# 添加装饰功能。
print("开始记录日志信息...")
self.__fn()
@Logger
def login():
print("完成登录")
if __name__ == '__main__':
login()
# 开始记录日志信息...
# 完成登录
__call__()
方法。import time
def time_cost(fn):
def inner():
begin = time.time()
fn()
# 当前时间减去开始时间。
cost = time.time() - begin
print(f"函数执行花费={cost}s")
return inner
@time_cost
def sample():
# 线程睡眠 2s。
time.sleep(2)
if __name__ == '__main__':
sample()
# 函数执行花费=2.003966808319092s
def logger(fn):
def inner(id):
print("开始记录日志信息...")
print(f"用户查询id={id}")
result = fn(id)
print(f"用户查询结果={result}")
print(f"日志记录完成,准备返回结果!")
return result
return inner
@logger
def query(user_id):
msg = {}
if user_id is not None:
msg = {
"user": "xxx",
"age": 18
}
return msg
if __name__ == '__main__':
query(111)
# 开始记录日志信息...
# 用户查询id=111
# 用户查询结果={'user': 'xxx', 'age': 18}
# 日志记录完成,准备返回结果!
Annotation
)并不是同一回事,和 C# 中的特性(Attribute
)也不一样,完全是两个概念。wrapper()
,意义在于包装。函数只有在被调用时才会发挥其作用。比如 @logging
装饰器可以在函数执行时额外输出日志,@cache
装饰过的函数可以缓存计算结果等等。“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。