闭包
def test(x, y):
def t1(z):
return x * z + y
return t1
t = test(2, 3)
print(t(3))
print(t(4))
闭包修改外部外部嵌套函数的变量值
# 错误案例
def test():
msg=10
def t1():
print("%d"%msg)
msg=100
print("%d" % msg)
return t1
t = test()
t()
"""
此时,第一个msg不会输出10而是直接报错,因为python会认为
t1函数扫描发现下面定义的有msg,类似于声明提升,但是没赋值,所以不允许使用
如果想使用,加上nonlocal,如下面的案例
"""
# nonlocal适用于嵌套函数中内部函数修改外部变量的值(该属性python3专用)
def counter(start=0):
def incr():
nonlocal start
start += 1
return start
return incr
c1 = counter(5)
print(c1())
print(c1())
c2 = counter(50)
print(c2())
print(c2())
print(c1())
print(c1())
print(c2())
print(c2())
python2等效案例
def counter(start=0):
count=[start]
def incr():
count[0] += 1
return count[0]
return incr
c1 = closeure.counter(5)
print(c1()) # 6
print(c1()) # 7
c2 = closeure.counter(100)
print(c2()) # 101
print(c2()) # 102
装饰器
基本案例
def t1(func):
print("装饰器的装饰过程是在代码加载的时候就执行的")
def t2():
return func
return t2
@t1
def T():
print("t执行")
return "haha"
t = T()
print(t())
"""
执行的结果:
装饰器的装饰过程是在代码加载的时候就执行的
t执行
haha
说明:第一句输出,即使在t=T()没写的情况下也会执行,是解释器的自主行为
"""
装饰器的基本原理(闭包)
def t1(func):
def t2():
return func
return t2
def T():
print("t执行")
return "haha"
T = t1(T)
t = T()
print(t())
"""
执行的结果:
t执行
haha
"""
综上所述,案例一的T()相当于案例二的T=t1(T)
多装饰器装饰同一个函数
def t1(func):
print("t1初始化")
def t2():
print("t2执行")
func()
return t2
def t3(func):
print("t3初始化")
def t4():
print("t4执行")
func()
return t4
@t3
@t1
def T():
print("T执行")
t = T()
"""
执行的结果:
t1初始化
t3初始化
t4执行
t2执行
T执行
说明:初始化是解释器从上到下,所以到@t3时候,进行装饰,但是装饰必须先有函数传入(联想闭包)
所以肯定先去获取函数,所以执行到@t1,所以t1初始化先执行
但是装饰完毕之后,代码在一个内存中合并起来,是从上倒下的,所以t4先执行
"""
被装饰器的函数有参数
def t1(func):
def t2(a, b):
print("t2执行")
func(a, b)
return t2
@t1
def T(a, b):
print("T执行", a, b)
T(1,2)
"""
执行的结果:
t2执行
T执行 1 2
"""
被装饰的函数有不定长参数
def t1(func):
def t2(*args, **kwargs):
func(*args, **kwargs)
return t2
@t1
def T(*args, **kwargs):
print("T执行", args,kwargs)
T(1,2,3,num=10)
"""
执行的结果:
T执行 (1, 2, 3) {'num': 10}
"""
带参数的装饰器
def t0(msg="hello"):
def t1(func):
def t2(*args, **kwargs):
func(*args, **kwargs)
print(msg)
return t2
return t1
@t0("zengqiang")
def T(*args, **kwargs):
print("T执行", args, kwargs)
T(1, 2, 3, num=10)
"""
执行的结果:
T执行 (1, 2, 3) {'num': 10}
zengqiang
总结:带参数的装饰器,比一般装饰器要多一层函数定义
"""
类装饰器(扩展)
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 call() 方法,那么这个对象就是callable的
class Test():
def __call__(self):
print('call me!')
t = Test()
t() # call me
类装饰器
class Test(object):
def __init__(self, func):
print("---初始化---")
print("func name is %s"%func.__name__)
self.__func = func
def __call__(self):
print("---装饰器中的功能---")
self.__func()
#说明:
#1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
# 并且会把test这个函数名当做参数传递到__init__方法中
# 即在__init__方法中的属性__func指向了test指向的函数
#
#2. test指向了用Test创建出来的实例对象
#
#3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
#
#4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
# 所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体
@Test
def test():
print("----test---")
test()
showpy()#如果把这句话注释,重新运行程序,依然会看到"--初始化--"
"""
运行结果如下:
---初始化---
func name is test
---装饰器中的功能---
----test---
"""