闭包
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
案例:
# 闭包
def print_info(name, age):
def info(sex):
print(f"姓名:{name},年龄:{age},性别:{sex}")
# 只是将一个内部方法引用返回,记住不能加括号,如果加括号就变成了执行函数了
return info
# 接收的是一个函数引用
show_info = print_info("zhangsan", 18)
show_info("女")
打印结果:
注意:函数,匿名函数(Lambda),闭包,对象,当做实参的区别是:
1.匿名函数能够完成基本简单功能,传递是这个函数的引用,只有功能
2.普通函数能够完成复杂的功能,传递这个函数的引用,只有功能
3.闭包能够完成将较为复杂的功能,并且每个函数都需要返回闭包函数的引用,传递是这个闭包中的函数以及数据,因此传递功能+数据
4.对象能够完成最复杂的功能,传递是很多数据+很多功能,因此传递是功能+数据
修改闭包上级函数的局部变量
修改全局变量使用global,那么要修改闭包上级函数的局部变量使用nonlocal语法:
nonlocal 变量名
代码:
# 修改闭包局部变量
def print_info(name, age):
number = 100
def info(sex):
# 修改上级函数变量使用
nonlocal number
print(f"姓名:{name},年龄:{age},性别:{sex},number:{number}")
number = 10
print(f"姓名:{name},年龄:{age},性别:{sex},number:{number}")
# 只是将一个内部方法引流返回
return info
# 接收的是一个函数引用
show_info = print_info("zhangsan", 18)
show_info("女")
打印结果:
注意:如果在闭包中使用number变量,之后在定义一个number变量时,python解释器会报错,如下代码所示
装饰器
总体来说装饰器主要是来让代码更加简短,也方便维护代码而不需要修改原有已经调用的函数中的代码。也就是将复杂的代码可以变得很简洁。
自定义一个装饰器的演变代码:
# 创建一个闭包
def fun_01(func):
def a():
print("我是闭包============")
# 执行func方法
func()
return a
# 创建一个普通函数
def fun_02():
print("我是func_02")
# 将接收的变量名与上面的函数名相同,这个时候fun_02就有了新的指向为:fun_01
# 而参数fun_02已经将原有的函数引用传递给了fun_01
fun_02 = fun_01(fun_02)
fun_02()
以上的代码看上去有点绕,也不方便理解,现在使用装饰器方式来简化代码
# 创建一个闭包
def fun_01(func):
def a():
print("我是闭包============")
# 执行func方法
func()
return a
# 装饰器
@fun_01
def fun_02():
print("我是func_02")
fun_02()
打印结果如上图一样
多参数的装饰器
# 创建一个闭包
def fun_01(func):
# 这里的*args, **kwargs表示形参
def a(*args, **kwargs):
print("我是闭包============")
# 执行func方法,这里的*args, **kwargs表示拆包
func(*args, **kwargs)
return a
# 装饰器
@fun_01
def fun_02(num, *args, **kwargs):
print(f"{num},{args},{kwargs}")
fun_02(1, 2, 3, name="张三")
打印结果:
装饰器返回值
代码:
# 创建一个闭包
def fun_01(func):
# 这里的*args, **kwargs表示形参
def a(*args, **kwargs):
print("我是闭包============")
# 执行func方法,这里的*args, **kwargs表示拆包,
# 闭包返回的值就是装饰器的返回值
return func(*args, **kwargs)
return a
# 装饰器返回值
@fun_01
def fun_02(num, *args, **kwargs):
print(f"num={num},**args={args},**kwargs={kwargs}")
return "我是装饰器"
test = fun_02(1, 2, 3, name="张三")
print("调用fun_02的返回值", test)
打印结果:
注意:如果调用fun_02装饰器有返回值的函数,必须在闭包中也需要有返回return func(*args, **kwargs),才有效,否则将返回None。通用装饰器一般可以将闭包中的函数都写上返回值,因为它不会影响调用方。
多装饰器
代码:
# 创建一个闭包
def fun_01(func):
def a():
return "<td>" + func() + "</td>"
return a
# 创建一个闭包
def fun_02(func):
def a():
return "<h1>" + func() + "</h1>"
return a
# 装饰器返回值
@fun_01
@fun_02
def fun_03():
return "我是装饰器"
print(fun_03())
打印结果:
注意:哪个装饰器在前面就先执行哪个装饰器.
类装饰器
class MyClass(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func
# 类当做装饰器的原始代码 func = MyClass(func)
@MyClass
def func():
return "我是装饰器"
# func()获取的就是一个方法引用,再加上一个括号也可以执行
print(func()())
带参数的装饰器
代码:
def set_level(number):
def set_func(func):
def call_func():
if number == 1:
print(f"=========调用的number={number}==========")
elif number == 2:
print(f"=========调用的number={number}==========")
# 返回被装饰的函数
return func
return call_func
return set_func
# 带参数的装饰器,先执行set_level方法,有一个参数,
# 返回的是set_func()函数的引用,最后还是调用的这个@set_func装饰器
# 这样的好处就是固定一些不可修改的值
@set_level(1)
def func1():
return "我是装饰器func1"
@set_level(2)
def func2():
return "我是装饰器func2"
# func()获取的就是一个方法引用,再加上一个括号也可以执行
print(func1()())
print(func2()())
注意:带有参数的装饰器,只是为了固定一些不可更改的参数,最终还是返回的原始装饰器的引用@set_func
作者:阿超
原创公众号:『Python日常笔记』,专注于 Python爬虫等技术栈和有益的程序人生,会将一些平时的日常笔记都慢慢整理起来,也期待你的关注和阿超一起学习,公众号回复【1024】优质资源。