闭包、装饰器
闭包的概念及基本使用
- 闭包的概念:在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包
- 闭包构成的条件:
- 存在函数的嵌套关系
- 内层函数引用了外层函数的临时变量
- 外层函数返回内层函数的引用(地址)
闭包中变量问题
- 内层定义了和外层同名的变量,内层优先使用内层定义的变量,即使定义的变量的代码在内层的最后面
- 解决办法:当内层存在和外层同名变量,而且内层需要使用外层定义的变量,此时应该使用 nonlocal 关键字进行约束
def function_out(num):
print("function_out num = ", num)
def function_in(num_in):
nonlocal num
num += num_in
print("-----function_in-----num=", num)
print("-----function_in-----num_in=", num_in)
return function_in
f = function_out(10) # 获取内层函数的地址保存到f变量中
# function_out num = 10
f(20)
# -----function_in-----num= 10
# -----function_in-----num_in= 20
function_out num = 10
-----function_in-----num= 30
-----function_in-----num_in= 20
装饰器
- 装饰器的作用:不修改源代码的基础上,给函数增加新的功能
- 装饰器使用:
- 存在闭包
- 需要装饰的函数
- 写法:
- @闭包的外层函数名
def function_out(func):
def function_in():
print("---开始验证---")
func()
return function_in
# @function_out 装饰了login() 函数,底层:login = function_out(login)
@function_out
def login():
print("---开始登录---")
login()
---开始验证---
---开始登录---
装饰有参数的函数
- 普通参数
待装饰
@functioin_out
def login(num):
注意:
1)function_in(参数)
2)func(参数)
- 可变参数
待装饰
@functioin_out
def login(*args, **kwargs):
注意:
1)function_in(*args, **kwargs)
2)func(*args, **kwargs)
错误用法:
func(args,kwargs)
# 待装饰函数
def function_out(func):
def function_in(num):
print("---开始验证---")
func(num)
return function_in
@function_out
def login(num):
print("---开始登录--- num = ", num)
login(10)
---开始验证---
---开始登录--- num = 10
# 待装饰函数
def function_out(func):
def function_in(*args, **kwargs):
print("---开始验证function_in---args = ", args)
print("---开始验证function_in---kwargs = ", kwargs)
func(*args, **kwargs)
return function_in
@function_out
def login(*args, **kwargs):
print("---开始登录login--- args = ", args)
print("---开始登录login--- kwargs = ", kwargs)
login(10,20,30)
login(10, a = 20)
---开始验证function_in---args = (10, 20, 30)
---开始验证function_in---kwargs = {}
---开始登录login--- args = (10, 20, 30)
---开始登录login--- kwargs = {}
---开始验证function_in---args = (10,)
---开始验证function_in---kwargs = {'a': 20}
---开始登录login--- args = (10,)
---开始登录login--- kwargs = {'a': 20}
装饰有返回值的函数
- 待装饰的函数必须有返回值(return)
- 闭包的内层函数 func(*args,*kwargs) 改为 return func(args,**kwargs)
# 待装饰函数
def function_out(func):
def function_in(*args, **kwargs):
print("---开始验证function_in---args = ", args)
print("---开始验证function_in---kwargs = ", kwargs)
return func(*args, **kwargs)
return function_in
@function_out
def login(*args, **kwargs):
print("---开始登录login--- args = ", args)
print("---开始登录login--- kwargs = ", kwargs)
return args,kwargs
login(10, a = 20)
print()
print(login(20, a = 30))
---开始验证function_in---args = (10,)
---开始验证function_in---kwargs = {'a': 20}
---开始登录login--- args = (10,)
---开始登录login--- kwargs = {'a': 20}
---开始验证function_in---args = (20,)
---开始验证function_in---kwargs = {'a': 30}
---开始登录login--- args = (20,)
---开始登录login--- kwargs = {'a': 30}
((20,), {'a': 30})
在原装饰器上设置外部变量
- 作用:向装饰器内部传递参数
- 使用:
def test(path):
print(path)
def function_out(func):
"""外层函数"""
print("function_out path= ", path)
def function_in():
print("----开始验证----")
func()
# 返回内层函数的引用
return function_in
# 返回装饰器的引用
return function_out
- 使用:@test("login.py")
- test("login.py") ---> function_out
- @ 第一步的结果 ---> @ founction_out
def test(path):
print(path)
def function_out(func):
"""外层函数"""
print("function_out path =", path)
def function_in():
print("----开始验证----")
func()
# 返回内层函数的引用
return function_in
# 返回装饰器的引用
return function_out
@test("login.py") # 先执行 test("login.py") ---> function_out 引用 ---> @第一步的结果 @function_out
# @function_out
def login():
print("---开始登录login---")
@test("注册")
def register():
print("---开始注册---")
login()
register()
login.py
function_out path = login.py
注册
function_out path = 注册
----开始验证----
---开始登录login---
----开始验证----
---开始注册---
多重装饰器
- 多重装饰器:给一个函数进行多次装饰
- 装饰原则:就近原则(靠近待装饰函数的先装饰,随后一层一层装饰)
<b>
helloworld-1</b>
| <i>
helloworld-2</i>
| <i><b>
helloworld-3</b></i>
# 定义一个让文字加粗的装饰器
def makeBlod(func):
def function_in():
return "<b>"+func()+"</b>"
return function_in
# 定义一个让文字倾斜的装饰器
def makeItalic(func):
def function_in():
return "<i>"+func()+"</i>"
return function_in
@makeBlod
def test():
return "helloworld-1"
@makeItalic
def test2():
return "helloworld-2"
@makeItalic # 倾斜后装饰
@makeBlod # 就近原则,加粗先装饰
def test3():
return "helloworld-3"
print(test()) # <b>helloworld-1</b>
print(test2()) # <i>helloworld-2</i>
print(test3()) # <i><b>helloworld-3</b></i>
<b>
helloworld-1</b>
<i>
helloworld-2</i>
<i><b>
helloworld-3</b></i>
类装饰器
- 作用:使用一个类为一个函数装饰
-
类的书写:必须有两个方法
- init 方法,必须接收 装饰器传递的参数 func
- call 方法, self.func()
-
格式:@类名
待装饰的函数
login = Test(login)
- 对象名() 调用对象的 call()
from typing import Any
class Test(object):
def __init__(self) -> None:
print("---init---方法")
def run(self):
print("---正在运行---")
def __call__(self, *args: Any, **kwds: Any) -> Any:
print("---call---方法")
# 创建对象
test = Test()
# test.run()
# 当对象名(),此时会去调用类中的__call__()方法
test()
---init---方法
---call---方法
# 装饰器类的使用
class Test(object):
def __init__(self, func):
print("---init---方法")
print("---func---", func)
# func 是login函数的引用
self.func = func
def run(self):
print("---正在运行---")
def __call__(self, *args, **kwds):
print("---call---方法",*args, **kwds)
print("---func---", self.func)
self.func()
@Test # login = Test(login),
def login():
print("---开始登录---")
login()
---init---方法
---func---
---call---方法
---func---
---开始登录---