1.闭包函数:
1.1 什么是闭包?
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包
1.2 闭包的构成
- 在函数嵌套(函数里面再定义函数)的前提下
- 内部函数使用了外部函数的变量(还包括外部函数的参数)
- 外部函数返回了内部函数
定义:内层函数对外层函数非全局变量的引用,就叫做闭包函数。
1.闭包会一直存在内存当中,不会因为函数执行结束而被释放。
1.3 闭包的作用
1. 保护数据安全、保护数据干净性
闭包的目的:要接受被装饰的函数和被装饰函数需要的参数
1.4 闭包基本模型:
def wrapper():
name = 'xiongsheng'
def inner():
print(name)
inner()
wrapper()
======================
xiongsheng
判断是否为闭包函数:
可以通过print(函数名.__closure__ )看是否有<cell xxxx xxx >类似信息打印,如果有就说明该函数为闭包函数。
name = 'xiongsheng'
def wrapper():
def inner():
print(name)
inner()
print(inner.__closure__) # None 说明该函数不是闭包函数
wrapper()
===========================
None
name = 'xiongsheng'
def wrapper(x):
x = name
def inner():
print(x)
inner()
print(inner.__closure__) # <cell at 0x0000000000130C48: str object at 0x0000000001E8A260> 有类似的打印信息说明该函数为闭包函数
wrapper(name)
=================================================
xiongsheng
(<cell at 0x02B77490: str object at 0x00FF73A0>,)
1.5 应用场景:
获取网页内容
from urllib.request import urlopen
def zhua(url):
#url='www.baidu.com'
def get():
return urlopen(url).read()
return get
f=zhua('www.baidu.com')
f()
1.6 闭包的使用
# 外部函数
def config_name(name):
# 内部函数
def inner(info):
print(name + ": " + info)
return inner
tom = config_name("Tom") # 创建tom闭包实例对象
jerry = config_name("jerry") # 创建jerry闭包实例对象
print(id(tom))
print(id(jerry)) # 内存地址不同, 相当于创建了2个不同的闭包,
tom("你好! jerry")
tom("你好, 在吗?") # 如果执行tom闭包,因为已经保存了name参数,那么以后在输入的时候都是,tom说:xxx
jerry("不在, 不和你玩!")
1.7 修改闭包内使用的外部变量
# 1.函数嵌套
def func_out(): # 外部函数的定义
num = 10 # 定义一个变量
def inner(): # 内部函数的定义
nonlocal num # 在闭包内部修改外部函数的变量
num = 40
result = num + 10 # 2.内部函数必须使用外部函数的变量
print(result)
print("修改前的外部变量:", num)
inner()
print("修改后的外部变量:", num)
# print("打印结果:", result)
# 外部函数返回内部函数的地址
# 3. 外部函数要返回内部函数,这个使用了外部函数变量的内部函数称为闭包
return inner # 注意:没有括号,加上括号,就是函数调用
# 获取闭包的对象
# 这个func_new 就是闭包
# func_new函数执行的结果等于inner函数执行的结果
func_new = func_out()
# 执行闭包
func_new()
总结:一个函数内部如果需要某个名字关系,有两种方法:
1、通过传参的方式
2、通过闭包的方式,在该函数外层包一层作用域(即再定义一个函数),将需要的名字关系包在外部函数中
2. 装饰器:
2.1 装饰器的定义
定义:给已有函数增加额外功能的函数,它本质上就是一个闭包函数
装饰器的功能特点:
1.不修改已有函数的源代码
2.不修改已有函数的调用方式
3.给已有函数增加额外的功能
**功能:**在遵循原则的前提,为被装饰对象添加上新功能。
2.2 装饰器的基本模型
def wrapper(f):
def inner(*args,**kwargs):
"""被装饰函数执行之前的操作"""
ret = f(*args,**kwargs)
"""被装饰函数执行之后的操作"""
return ret
return inner
2.3 装饰器的示例代码
# 添加一个登录验证的功能
def check(fn):
def inner():
print("请先登录....")
fn()
return inner
def comment():
print("发表评论")
# 使用装饰器来装饰函数
comment = check(comment)
comment()
# 装饰器的基本雏形
# def decorator(fn): # fn:目标函数.
# def inner():
# '''执行函数之前'''
# fn() # 执行被装饰的函数
# '''执行函数之后'''
# return inner
代码说明:
1.闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
2.写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。
2.4 装饰器的语法糖写法
语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰
说明:
@check 等价于 comment = check(comment)
装饰器的执行时间是加载模块时立即执行。
总结
装饰器本质上就是一个闭包函数,它可以对已有函数进行额外的功能扩展。
装饰器的语法格式:
# 装饰器
def decorator(fn): # fn:被装饰的目标函数.
def inner():
'''执行函数之前'''
fn() # 执行被装饰的目标函数
'''执行函数之后'''
return inner
2.5 通用装饰器的使用
1.装饰带有参数的函数
# 添加输出日志的功能
def logging(fn):
def inner(num1, num2):
print("--正在努力计算--")
fn(num1, num2)
return inner
# 使用装饰器装饰函数
@logging
def sum_num(a, b):
result = a + b
print(result)
sum_num(1, 2)
2.装饰带有返回值的函数
# 添加输出日志的功能
def logging(fn):
def inner(num1, num2):
print("--正在努力计算--")
result = fn(num1, num2)
return result
return inner
# 使用装饰器装饰函数
@logging
def sum_num(a, b):
result = a + b
return result
result = sum_num(1, 2)
print(result)
3.装饰带有不定长参数的函数
# 添加输出日志的功能
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
fn(*args, **kwargs)
return inner
# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value
for value in kwargs.values():
result += value
print(result)
sum_num(1, 2, a=10)
4.通用装饰器
# 添加输出日志的功能
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
result = fn(*args, **kwargs)
return result
return inner
# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value
for value in kwargs.values():
result += value
return result
@logging
def subtraction(a, b):
result = a - b
print(result)
result = sum_num(1, 2, a=10)
print(result)
subtraction(4, 2)
6.通用装饰器的语法格式:
# 通用装饰器
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
result = fn(*args, **kwargs)
return result
return inner
2.6 多个装饰器的使用
def make_div(func):
"""对被装饰的函数的返回值 div标签"""
def inner():
return "<div>" + func() + "</div>"
return inner
def make_p(func):
"""对被装饰的函数的返回值 p标签"""
def inner():
return "<p>" + func() + "</p>"
return inner
# 装饰过程: 1 content = make_p(content) 2 content = make_div(content)
# content = make_div(make_p(content))
@make_div
@make_p
def content():
return "人生苦短"
result = content()
print(result)
代码说明:
多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
小结
多个装饰器可以对函数进行多个功能的装饰,装饰顺序是由内到外的进行装饰
2.7 带有参数的装饰器
在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。
# 添加输出日志的功能
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)
2.8 类装饰器的使用
class Check(object):
def __init__(self, fn):
# 初始化操作在此完成
self.__fn = fn
# 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
def __call__(self, *args, **kwargs):
# 添加装饰功能
print("请先登陆...")
self.__fn()
@Check
def comment():
print("发表评论")
if __name__ == '__main__':
comment()
代码说明:
@Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
在call方法里进行对fn函数的装饰,可以添加额外的功能。
小结
想要让类的实例对象能够像函数一样进行调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable)
类装饰器装饰函数功能在call方法里面进行添加