闭包
首先知道闭包函数的语法特征:
- 函数嵌套定义
- 外部函数返回内部函数的引用
- 内部函数可以调用外部函数的自由变量
外部函数的作用是创建内部函数并且返回内部函数的引用。
def line(k, b):
"""外部函数的作用: 创建内部函数并且返回内部函数的引用"""
def line_in(x):
y = k * x + b
print(y)
return line_in
line1 = line(1,1)
line1(10)
line1(20)
line2 = line(2,2)
line2(10)
line1(10)
line1和line2都是inner函数代码的引用 为什么运行结果不一样呢?
因为两个闭包为不同的对象且使用自由变量不一样,所以闭包运算的结果就不一样
闭包的内部函数可以修改自由变量的值,在内部函数使用nonlocal 关键字
def line(k, b):
"""外部函数的作用: 创建内部函数并且返回内部函数的引用"""
# d = [k]
def line_in(x):
# py3中 nonlocal关键字用以修改 自由变量<不可变类型>
nonlocal k
k = k + 1
y = k * x + b
# py2 py3都支持的方式 - 间接使用 并没有修改真正k的值
# d[0] += 1
# y = d[0] * x + b
print(y)
return line_in
l1 = line(1,1)
# 打印内部函数的自由变量
print(l1.__closure__)
print(l1.__closure__[0])
print(l1.__closure__[0].cell_contents)
l1(99)
(<cell at 0x000001EEFC387738: int object at 0x000000006E556DE0>, <cell at 0x000001EEFC387C48: int object at 0x000000006E556DE0>)
<cell at 0x000001EEFC387738: int object at 0x000000006E556DE0>
1
199
装饰器
使用装饰器,提供一个语法糖@(Synatax Sugar)。
装饰器是基于闭包函数,遵循封闭开放的代码设计原则,在保证不修改原代码的基础上扩展代码功能。
装饰器其实是闭包的特例, 其外部函数传的参数是函数名而已。
def check(func):
#def check(*args, **kwargs):
def wrapper(*args, **kwargs):
print("添加验证功能")
func(*args, **kwargs)
print("添加结束处理功能")
return wrapper
@check
def func1(num):
"""不容易发生改变的功能"""
print("this is fun1")
print("fun1 num %s" % num)
@check
def func2(num1,num2):
print("this is func2")
print("func2 num1 %s num2 %s" % (num1, num2))
# func1 = check(func1)
func1(2)
func2(3, 4)
添加验证功能
this is fun1
fun1 num 2
添加结束处理功能
添加验证功能
this is func2
func2 num1 3 num2 4
添加结束处理功能
check是基于闭包的装饰器函数,func是被装饰函数的引用。
装饰器函数只能接收一个参数,即被装饰函数的引用。
装饰器可以装饰有任意参数的函数。
使用装饰器的方式,@装饰器名 放在被装饰函数的上一行。
装饰器的灵魂代码即为: func1 = check(func1)
@check 就是对灵魂代码的封装,很方便
装饰器demo 统计冒泡排序耗时
import time
import random
def resume_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
# 接收func函数的返回值
result = func(*args, **kwargs)
end_time = time.time()
cost_time = end_time - start_time
print("cost_time:%s" % cost_time)
# 返回func函数的返回值
return result
return wrapper
@resume_time
def bubble_sort(list):
time.sleep(0.6)
n = len(list)
for i in range(n-1):
for j in range(n-1-i):
if list[j] > list[j+1]:
list[j], list[j+1] = list[j+1], list[j]
return list
lyst = [i for i in range(1000)]
random.shuffle(lyst)
print(bubble_sort(lyst))
多个装饰器修饰一个函数
# 定义函数:完成包裹数据
def makeBold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
# 定义函数:完成包裹数据
def makeItalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makeBold
def test1():
return "hello world-1"
@makeItalic
def test2():
return "hello world-2"
@makeBold
@makeItalic
def test3():
return "hello world-3"
print(test1())
print(test2())
print(test3())
<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>
调用test3() 相当于makeBold(makeItalic(test3))()
由于装饰器函数只能接收一个参数,若想要多传入几个参数,使装饰器功能更加丰富如何做?
可以使用装饰器工厂,传入一个flag参数,当flag为0时耗时时间取浮点形式,flag为1时取整型。
import time
import random
# 接收参数生成装饰器
def factory(flag):
# flag 向下传递
def resume_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
cost_time = end_time - start_time
# 接收上层传来的flag
if flag == 0:
print("cost_time:%s" % cost_time)
elif flag == 1:
print("cost_time:%d" % int(cost_time))
return result
return wrapper
return resume_time
# 调用factory函数传入flag为1
@factory(1)
def bubble_sort(list):
time.sleep(0.6)
n = len(list)
for i in range(n-1):
for j in range(n-1-i):
if list[j] > list[j+1]:
list[j], list[j+1] = list[j+1], list[j]
return list
lyst = [i for i in range(1000)]
random.shuffle(lyst)
print(bubble_sort(lyst))
下面为装饰过程
1. 调用factory(1)
2. 将步骤1得到的返回值,即resume_time返回, 然后resume_time(bubble_sort)
3. 将resume_time(bubble_sort)的结果返回,即wrapper
4. 即bubble_sort = wrapper,即bubble_sort现在指向wrapper
类装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个callbale对象作为参数,然后返回一个callable对象。在Pthon中一般callable对象都是函数,但还有一种情况就是某个对象重写了__call__()方法,那么这个对象就是callable的。
class Test(object):
def __init__(self, func):
print("---初始化---")
print("func name is %s"%func.__name__)
self.__func = func
def __call__(self):
print("---装饰器中的功能---")
self.__func()
@Test
def test():
print("----test---")
test()
---初始化---
func name is test
---装饰器中的功能---
----test---说明:
1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
并且会把test这个函数名当做参数传递到__init__方法中
即在__init__方法中的属性__func指向了test指向的函数
2. test指向了用Test创建出来的实例对象 test = Test(test)
3. 当在使用test()进行调用时,就相当于 Test对象(),因此会调用这个对象的__call__方法
4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体