闭包
在Python中什么是闭包呢?要想了解闭包首先来了解一下函数名的作用:
①函数名存放的是函数所在空间的地址
②函数名()执行函数名所存放空间地址中的代码
③func01 = func02函数名可以像普通变量一样赋值,func01() 等价于 func02()
结论:函数可以像普通变量一样作为参数使用
函数被调用完之后,函数内定义的变量会被都销毁,但是有时候又想用到这些变量该怎么办呢?这时候就可以用闭包来解决这个问题。首先闭包的定义:
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
从定义中可以看出闭包的构成条件有一下三点:
①在函数嵌套(函数里面再定义函数)的前提下
②内部函数使用了外部函数的变量(还包括外部函数的参数)
③外部函数返回了内部函数
由此可以看出闭包的作用:
闭包不仅可以保存外部函数的变量还可以提高代码的复用性。
我们可以通过上面的定义写出一个标准的闭包格式:
# 外部函数
def test1(a):
b = 10
# 内部函数
def test2():
# 内部函数使用了外部函数的变量或者参数
print(a, b)
# 返回内部函数, 这里返回的内部函数就是闭包实例
return test2
注意在闭包内修改外部变量就需要使用到nonlocal关键字
我们来看下面两段代码的对比就可以知道了:
代码1:
# 定义一个外部函数
def func_out(num1):
# 定义一个内部函数
def func_inner(num2):
# 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
num1 = 10
# 内部函数使用了外部函数的变量(num1)
result = num1 + num2
print("结果是:", result)
print(num1)
func_inner(1)
print(num1)
# 外部函数返回了内部函数,这里返回的内部函数就是闭包
return func_inner
# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)
执行结果:
1
结果是: 11
1
结果是: 12
代码2:
# 定义一个外部函数
def func_out(num1):
# 定义一个内部函数
def func_inner(num2):
# 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
nonlocal num1 # 告诉解释器,此处使用的是 外部变量a
# 修改外部变量num1
num1 = 10
# 内部函数使用了外部函数的变量(num1)
result = num1 + num2
print("结果是:", result)
print(num1)
func_inner(1)
print(num1)
# 外部函数返回了内部函数,这里返回的内部函数就是闭包
return func_inner
# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)
执行结果:
1
结果是: 11
10
结果是: 12

装饰器
装饰器的作用:
在不改变原有函数的源代码的情况下,给函数增加新的功能
装饰器的功能特点:
①不修改已有函数的源代码
②给已有函数增加额外的功能
装饰器符合了 开发中的封闭开放原则
通用装饰器的语法格式:
# 装饰器的基本雏形
# def decorator(fn): # fn:目标函数.
# def inner():
# '''执行函数之前'''
# fn() # 执行被装饰的函数
'''执行函数之后'''
return inner
装饰器的语法糖用法: @装饰器名称,同样可以完成对已有函数的装饰操作。
不使用语法糖:
# 添加一个登录验证的功能
def check(fn):
def inner():
print("请先登录....")
fn()
return inner
def comment():
print("发表评论")
# 使用装饰器来装饰函数
comment = check(comment)
comment()
使用语法糖:
# 添加一个登录验证的功能
def check(fn):
print("装饰器函数执行了")
def inner():
print("请先登录....")
fn()
return inner
# 使用语法糖方式来装饰函数
@check
def comment():
print("发表评论")
comment()
说明:
- @check 等价于 comment = check(comment)
- 装饰器的执行时间是加载模块时立即执行。
- 多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
- 装饰器只能接收一个参数,并且还是函数类型。
装饰器只能接收一个参数,并且还是函数类型。那如果要写带有参数的装饰器如何写呢?
示例:
# 添加输出日志的功能
def logging(flag):
def decorator(fn):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
result = fn(num1, num2)
return result
return inner
# 返回装饰器
return decorator
# 使用装饰器装饰函数
@logging("+")
def add(a, b):
result = a + b
return result
@logging("-")
def sub(a, b):
result = a - b
return result
result = add(1, 2)
print(result)
result = sub(1, 2)
print(result)
装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。
类装饰器示例代码:
class Check(object):
def __init__(self, fn):
# 初始化操作在此完成
self.__fn = fn
# 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
def __call__(self, *args, **kwargs):
# 添加装饰功能
print("请先登陆...")
self.__fn()
@Check
def comment():
print("发表评论")
comment()
说明:
- @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
- 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
- 在call方法里进行对fn函数的装饰,可以添加额外的功能。
作者简介:Python菜鸟工程师,将在接下来的一段时间内与大家分享一些与Python相关的知识点。如若文中出现问题,各位大佬多多指点,互相学习。喜欢的关注一个吧!谢谢!
至此Python中的多任务和装饰器已经讲解完毕,需要XMIND的同学可以回复:多任务xmind 获取思维导图资料。
