1、函数嵌套
2、内部函数引用外部函数的参数或变量
3、外部函数返回内部函数的引用或调用
一般情况下,函数在return之后,会结束函数,返回值,同时释放内存(因为无引用)
一个函数的闭包函数,在外部函数结束之后,内部函数引用的外部函数的变量,会被存储下来,存储到内部函数的__closure__属性中。
# 例如
def A(a):
c = 999
def B(b):
d = a + b
return B
test = A(1) #指向B的内存地址
print(test)
print(test.__closure__) # 用test调用__closure__可以看到存在内存的变量,以元组的形式存在,由cell对象组成
print(test.__closure__[0].cell_contents) # 根据索引,cell_contents属性就是外部函数保存的变量
# 终端显示
<function A.<locals>.B at 0x00000233724AC430>
(<cell at 0x00000233723CF040: int object at 0x00007FFF24312730>,)
1
# 例如
def outer():
list1 = []
for k in range(3):
def inner():
return k * k
list1.append(inner)
return inner
print(outer()) # outer()即指向inner的内存地址
print(outer()()) # 即等价于inner(),返回k * k
print(outer().__closure__) # 当外部函数结束之后,查看内部函数内存里存了几个变量,可以看到是一个
print(outer().__closure__[0].cell_contents) # 查看该变量
# 终端显示
<function outer.<locals>.inner at 0x0000024041DCC550>
4
(<cell at 0x0000024041CEF040: int object at 0x00007FFF22982750>,)
2
# 例如
def outer():
list1 = []
for k in range(3):
def inner():
return k * k
list1.append(inner)
return list1
print(outer()) # 因为返回是list1,所以此处是一个列表,列表里面的元素是inner
print(outer().__closure__) # 注意:list1不是inner,只是list1里面的元素是inner,所以list1不能调用__closure__,此处会报错
print(outer()[0].__closure__[0].cell_contents) # outer()[0]表示取列表list1的第一个元素,即第一个inner,.__closure__[0]表示取内部的第一个变量,因为知道元组里面只有一个变量,.cell_contents表示查看该变量内容
print(outer()[1].__closure__[0].cell_contents)
print(outer()[2].__closure__[0].cell_contents)
# 终端显示
[<function outer.<locals>.inner at 0x000001156B0BC430>, <function outer.<locals>.inner at 0x000001156B0BC4C0>, <function outer.<locals>.inner at 0x000001156B0BC550>]
AttributeError: 'list' object has no attribute '__closure__' # 报错
2
2
2
装饰器:在原来的函数上起装饰作用,不改变原来函数作用,在原来函数基础上增加一些额外功能;
装饰器不是编码必须性
洗衣机工作时长计时
不使用装饰器
# 例如
import time
def outer(func):
def timer():
t1 = time.time()
func()
t2 = time.time()
print(f'洗衣机工作用时{t2 - t1}秒')
return timer
def washer():
print('洗衣机开始工作')
time.sleep(3)
print('洗衣机工作完成')
washer = outer(washer)
washer()
# 终端显示
洗衣机开始工作
洗衣机工作完成
洗衣机工作用时3.007417678833008秒
使用装饰器
# 例如
import time
def outer(func):
def timer():
t1 = time.time()
func()
t2 = time.time()
print(f'洗衣机工作用时{t2 - t1}秒')
return timer
@outer # 将outer作为装饰器修饰washer,此处outer不带参数
def washer():
print('洗衣机开始工作')
time.sleep(3)
print('洗衣机工作完成')
washer(3)
# 终端显示
洗衣机开始工作
洗衣机工作完成
洗衣机工作用时3.0132269859313965秒
装饰器原理,被装饰的函数定义完成之后,将该函数作为参数,传给装饰器,把它作为装饰器的实参来调用,然后装饰器返回的函数重新命名为与被装饰函数一样的函数名 (装饰器只起装饰作用,不改变原函数功能,所以装饰前后的函数名要一致)
# 例如
import time
def outer(delay):
def timer(func):
t1 = time.time()
func(delay)
t2 = time.time()
print(f'洗衣机工作用时{t2 - t1}秒')
return timer
@outer(3) # 带参数
def washer(delay):
print('洗衣机开始工作')
time.sleep(delay)
print('洗衣机工作完成')
washer()
# 终端显示
洗衣机开始工作
洗衣机工作完成
洗衣机工作用时3.00116229057312秒 # 前面表示正常工作
Traceback (most recent call last): # 后面是因为执行washer()的时候,washer接收得到的是none,不可被调用,如果将wahser()注释掉,程序自动运行
File "d:/shenlanclass/PythonFiles/day19/day1901.py", line 61, in <module>
washer()
TypeError: 'NoneType' object is not callable
PS D:\shenlanclass\PythonFiles>
增加功能,增加name
# 例如
import time
def outer(name):
def inner(func):
def timer(delay):
t1 = time.time()
func(delay)
t2 = time.time()
print(f'{name}你的衣服洗好了,洗衣机工作用时{t2 - t1}秒,快去拿!')
return timer
return inner
@outer('张三')
def washer(delay):
print('洗衣机开始工作')
time.sleep(delay)
print('洗衣机工作完成')
washer(3)
# 终端显示
洗衣机开始工作
洗衣机工作完成
张三你的衣服洗好了,洗衣机工作用时3.0018467903137207秒,快去拿!
注意:
装饰器函数是多个嵌套的时候(并非多个装饰器),最外一层装饰器的返回值要将被装饰函数的函数名作为实参,所以在从外往里数第二层的时候,函数的要以func(也就是函数)作为参数上传,否则会报错
# 例如
import time
class f1:
def __init__(self, func):
self.func = func
def __call__(self, delay):
t1 = time.time()
self.func(delay)
t2 = time.time()
print(f'你的衣服洗好了,洗衣机工作用时{t2 - t1}秒,快去拿!')
@f1
def washer(delay):
print('洗衣机开始工作')
time.sleep(delay)
print('洗衣机工作完成')
washer(3)
# 终端显示
洗衣机开始工作
洗衣机工作完成
你的衣服洗好了,洗衣机工作用时3.002544641494751秒,快去拿!
注意:
不带参数,类装饰器,在被装饰函数定义完之后,将被装饰函数的函数名放到类装饰器里当做实参去执行,因此,此时需要类装饰器在__init__的时候,传入参数func(实参传入被装饰函数)
# 例如
import time
class f1:
def __init__(self, name):
self.name = name
def __call__(self, func):
def inner(delay):
t1 = time.time()
func(delay)
t2 = time.time()
print(f'{self.name}你的衣服洗好了,洗衣机工作用时{t2 - t1}秒,快去拿!')
return inner
@f1('张三')
def washer(delay):
print('洗衣机开始工作')
time.sleep(delay)
print('洗衣机工作完成')
washer(3)
# 终端显示
洗衣机开始工作
洗衣机工作完成
张三你的衣服洗好了,洗衣机工作用时3.0059919357299805秒,快去拿!
注意:
带参数,类装饰器,将装饰器后面的参数传入装饰器进行初始化__init__,在被装饰函数定义完之后,将被装饰函数的函数名传入类装饰器,调用__call__,此时,__call__需要接收func(被装饰函数的函数名)作为参数
当使用多个装饰器的时候,被定义参数定义完成之后,作为参数上传到最近的装饰器,第一个装饰器返回值给第二个参数
# 例如
import time # 一
def deco(func): # 二
def wrapper1(*args): # 九
res = func(*args) # 十二
return res # 二十
return wrapper1 # 十
def timer(func): # 三
def wrapper2(*args): # 七
start_time = time.time() # 十三
res = func(*args) # 十四
end_time = time.time() # 十七
print("函数耗时:{}".format(end_time-start_time)) # 十八
return res # 十九
return wrapper2 # 八
@deco # 四
@timer # 五
def add(*args): # 六
time.sleep(2) # 十五
return sum(args) # 十六
print(add(3, 4, 5)) # 十一 # 二十一
# 十一,此时add = wrapper1,add(3, 4, 5)等价于wrapper1(3, 4, 5),对wrapper1(3, 4, 5)中的3,4,5封包成元组,跳到十二
# 十二,调用func,此时func = wrapper2,同时func接收到的参数是元组(3, 4, 5),*args对元组进行解包,成为不定长参数3, 4,5,跳到十三
# 十三,正常运行,但是在跳到十三的时候,调用wrapper2对接收到的不定长参数再次封包成为元组
# 十四,此时func = add,func接收不定长参数,对接收到的参数解包成为数字3, 4,5跳到十五
# 十五,add对接收到的不定长参数3, 4,5进行封包,执行睡眠
# 十六,执行求和函数,并返回,12,第十四步,res = 12,
# 十七,正常执行
# 十八,正常执行
# 十九,正常执行,返回res = 12
# 二十,十二部的函数返回值是12,即十二步,res = 12,执行return res,返回12
# 二十一,add(3, 4, 5)的返回值是12
# 终端显示
函数耗时:2.006530284881592
12
类装饰器
静态装饰器
将类中的方法装饰为只读属性,不可修改
不能传参数
对象只能是实例属性,不能是类属性
# 例如
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@property # 只读属性
def threshold(self):
return 18
def is_adult(self):
if self.age >= 18:
print(f'恭喜{self.name},成年了')
else:
print(f'{self.name}还没有成年')
p1 = Person('张三', 18)
p2 = Person('李四', 16)
p1.is_adult()
p2.is_adult()
print(p1.threshold)
p1.threshold = 16 #此处报错
print(p1.threshold)
# 终端显示
恭喜张三,成年了
李四还没有成年
18 # 查看属性
Traceback (most recent call last):
File "d:/shenlanclass/PythonFiles/day19/day1901.py", line 205, in <module>
p1.threshold = 16
AttributeError: can't set attribute #不能修改
属性返回函数
# 例如
def a(b): # 定义函数a
return b
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@property
def threshold(self):
return a # 返回函数a
def is_adult(self):
if self.age >= 18:
print(f'恭喜{self.name},成年了')
else:
print(f'{self.name}还没有成年')
p1 = Person('张三', 18)
p2 = Person('李四', 16)
p1.is_adult()
p2.is_adult()
# print(p1.threshold)
print(p1.threshold(1))
# 终端显示
恭喜张三,成年了
李四还没有成年
1