闭包函数,
闭包函数 = 函数嵌套定义+ 函数对象+ 名称空间与作用域
闭: 指的是该函数时定义在一个函数内部的函数
包; 指的是该函数访问了一个来之于外层函数的变量
例:
def func():
x = 3
def func():
print(x)
这就是闭包函数, 把x 封闭到局部空间中
为函数体传参:
1. 直接使用参数的形式
def wrapper(x):
print(x)
wrapper(2)
2. 把函数体想要的参数包给它
def outter(x):
# x = 111
def wrapper():
print(x)
return wrapper # 把函数wrapper返回到全局作用域中
调用时可以在全局调用
闭包函数实现了变量定义在函数内代码只能内部访问, 二外部访问不到
装饰器
什么是装饰器
装饰器就是装饰函数的一种功能, 对原函数进行装饰
装饰器的定义, 在不改变源代码情况下, 为函数添加新功能,
函数体和调用方式都不能改变
开放封闭原则
1. 被装饰函数index
def index(x,y):
print(x,y)
index(1,2)
2. 为index添加新功能 '运行时间计时'
import time
def index(x,y):
start = time.time()
time.sleep(1)
ends = time.time()
print(ends-start)
index(1,2)
改变了源代码, 不可行
3. def index(x,y):
print(x,y)
start = time.time()
index(1,2)
ends = time.time()
print(ends-start)
没改变源代码,也没改变调用方式
但执行起来麻烦, 如果有多处需要修改, 那么就很麻烦了
4. 定义一个装饰函数, 把函数放到装饰器内
原:
def index(x,y):
print(x,y)
def wrapper():
start = time.time()
index(1,2)
ends = time.time()
print(ends-start)
wrapper()
功能实现了, 但调用方式也跟着改变了, 不可行
5. 把wrapper函数内的index修改为参数形式
def index(x,y):
print(x,y)
def wrapper(func):
start = time.time()
func(1,2)
ends = time.time()
print(ends-start)
wrapper(index)
直接为wrapper函数传参的方式不行, 因为index是在全局, 而现在把index放到
局部中, 调用时还得先调用wrapper, 显然不符合, 不可行
6. 基于闭包函数吧wrapper函数想要传参包给index, 然后基于函数对象
把值也包给index,
def index(x,y):
print(x,y)
def wrapper(func):
def inner():
start = time.time()
func(1,2)
ends = time.time()
print(ends-start)
return inner
index = outter(index)
index()
这样大致的模仿传来传参方式了, 但如果我想把函数内的参数改变呢
还是不行, 继续改,
7. 这里使用到* 和 ** 的知识点了, 把func传参位置换成*和**
def index(x,y):
print(x,y)
def outter(func):
def wrapper(*args,**kwargs):
start = time.time()
func(*args,**kwargs)
ends = time.time()
print(ends -start)
return wrapper
index(1,2)
完美保证了不该源代码和调用方式,而且还可以装饰任意函数
这样就完善了, 怎么传参都行, 但是有没有想到, 如果函数有返回值呢,
作何处理, 下面处理
8. 在前方案改进, 把wrapper函数的返回值与被装饰函数报持一致
def index(x,y):
print(x,y)
return 123
def wrapper(func):
def inner(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
ends = time.time()
print(ends-start)
return res
return inner
ret = index(1,2)
print(ret)
这样既可不改源代码, 又没有改变调用方式,
基本装饰器就这么实现了
标准装饰器如下
def wrapper(func):
def inner(*args,**kwargs):
"""功能代码"""
res = func(*args,**kwargs)
"""功能代码"""
return res
return inner
语糖的使用
@装饰器
@wrapper
def func():
print('only')
了解:
文档注释,
把原函数的注释和文档一并拷贝到被装饰的函数中
用到functools 模块中的wraps
使用方法
在装饰器内接受函数的变量名头顶上加@wraps
例:
def wrapper(func):
@wraps # 完美
def inner(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs)
ends = time.time()
print(ends-start)
return res
return inner
叠加多个装饰器
例:
def deco1(func1):
def inner():
print(func1):
return inner
def deco2(func2):
def inner():
print(func2)
return inner
@deco1
@deco2
def index(x):
print(x)
多个装饰器叠加的方式, 直接在函数头顶加
简单概述
当程序运行遇到语法糖@ 是, 语法糖函数会马上把@下的函数名
返回给 '@函数' 然后运行监测 '@函数' ;但不会马上运行函数体代码
结论
加载顺序从下往上
执行顺序自上往下内层的函数
# 2、叠加多个装饰器
# 2.1 加载顺序:自下而上
# 2.2 执行顺序:自上而下运行内层的wrapper函数
# def deco1(func1): # func1 = wrapper2的内存地址
# def wrapper1(*args,**kwargs):
# print('wrapper1====>')
# res1=func1(*args,**kwargs)
# return res1
# return wrapper1
#
# def deco2(func2): # func2 = wrapper3的内存地址
# def wrapper2(*args,**kwargs):
# print('wrapper2====>')
# res2=func2(*args,**kwargs)
# return res2
# return wrapper2
#
# def deco3(func3): # func3 = 最原始的那个被装饰函数的内存地址
# def wrapper3(*args,**kwargs):
# print('wrapper3====>')
# res3=func3(*args,**kwargs)
# return res3
# return wrapper3
#
# # index=wrapper1的内存地址
# @deco1 # deco1(wrapper2的内存地址)=>wrapper1的内存地址
# @deco2 # deco2(wrapper3的内存地址)=>wrapper2的内存地址
# @deco3 # deco3(最原始的那个被装饰函数的内存地址)=>wrapper3的内存地址
# def index(x,y):
# print('index=>',x,y)
#
# index(1,2)
#
结果
# """
# wrapper1====>'
# wrapper2====>
# wrapper3====>
# index=>1,2
# """