闭包和装饰器相关
闭包参考:http://blog.csdn.net/marty_fu/article/details/7679297
装饰器参考1:https://segmentfault.com/a/1190000007321935 *****
装饰器参考2:http://blog.csdn.net/dreamcoding/article/details/8611578
1. 闭包中是不能修改外部作用域的局部变量的
>>> >>> def foo(num): ... def bar(): ... num = 3 ... print(num) ... print(num) ... bar() ... >>> foo(2) 2 3 >>>
2. 闭包中经典的错误代码
>>> >>> def foo(): ... a = 1 ... def bar(): ... a = a + 1 ... return a ... return bar ... >>> f = foo() >>> f <function foo.<locals>.bar at 0x000000000076B048> >>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in bar UnboundLocalError: local variable 'a' referenced before assignment >>>
解释:
python规则指定所有在赋值语句左面的变量都是局部变量,
则在闭包bar()中,变量a在赋值符号"="的左面,被python认为是bar()中的局部变量。
再接下来执行print c()时,程序运行至a = a + 1时,因为先前已经把a归为bar()中的局部变量,
所以python会在bar()中去找在赋值语句右面的a的值,结果找不到,就会报错。
解决办法:使用nonlocal来指定a不是闭包中的局部变量
>>> >>> def foo(): ... a = 1 ... def bar(): ... nonlocal a ... a = a + 1 ... return a ... return bar ... >>> f = foo() >>> f <function foo.<locals>.bar at 0x000000000076B0D0> >>> >>> f() 2 >>>
或者把 a 设定为一个容器:
>>> >>> def foo(): ... a = [1] ... def bar(): ... a[0] = a[0] + 1 ... return a[0] ... return bar ... >>> f = foo() >>> f <function foo.<locals>.bar at 0x000000000076B158> >>> >>> f() 2 >>>
3.为什么要使用闭包?
参考:https://zhuanlan.zhihu.com/p/26934085
闭包避免了使用全局变量,此外,闭包允许将函数与其所操作的某些数据(环境)关连起来。
这一点与面向对象编程是非常类似的,在面对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。
所有函数都有一个 __closure__属性,如果这个函数是一个闭包的话,那么它返回的是一个由 cell 对象 组成的元组对象。
cell 对象的cell_contents 属性就是闭包中的自由变量。
def adder(x):
def wrapper(y):
return x + y
return wrapper
adder5 = adder(5)
print(adder5(10)) # 15
print(adder.__closure__) # None
print(adder5.__closure__) # (<cell at 0x00000000006F6168: int object at 0x0000000059060250>,)
# for attr in dir(adder5.__closure__[0]):
# print(attr,getattr(adder5.__closure__[0],attr))
print(adder5.__closure__[0].cell_contents) # 5
这解释了为什么局部变量脱离函数之后,还可以在函数之外被访问的原因的,
因为它存储在了闭包的 cell_contents 中了。
4. 闭包的应用
# 1. 当闭包执行完后,仍然能够保持住当前的运行环境 >>> >>> origin = [0, 0] # 坐标系统原点 >>> legal_x = [0, 50] # x轴方向的合法坐标 >>> legal_y = [0, 50] # y轴方向的合法坐标 >>> def create(pos=origin): ... def player(direction,step): ... # 这里应该首先判断参数direction,step的合法性,比如direction不能斜着 走,step不能为负等 ... # 然后还要对新生成的x,y坐标的合法性进行判断处理,这里主要是想介绍闭 包,就不详细写了。 ... new_x = pos[0] + direction[0]*step ... new_y = pos[1] + direction[1]*step ... pos[0] = new_x ... pos[1] = new_y ... #注意!此处不能写成 pos = [new_x, new_y],原因在上文有说过 ... return pos ... return player ... >>> >>> player = create() # 创建棋子player,起点为原点 >>> print(player([1,0],10)) # 向x轴正方向移动10步 [10, 0] >>> print(player([0,1],20)) # 向y轴正方向移动20步 [10, 20] >>> print(player([-1,0],10)) # 向x轴负方向移动10步 [0, 20] >>>
# 2. 闭包可以根据外部作用域的局部变量来得到不同的结果,类似配置功能。
>>>
>>> def make_filter(keep):
... def the_filter(file_name):
... file = open(file_name)
... lines = file.readlines()
... file.close()
... filter_doc = [i for i in lines if keep in i]
... return filter_doc
... return the_filter
...
>>>
>>> filter = make_filter("pass")
>>> filter_result = filter("result.txt")
...
5. 装饰器
"""装饰器"""
def wrapper(func):
print('装饰器工作了')
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
'''
1.立即执行wrapper函数,并将下面装饰的函数当做参数传递进去
2.将wrapper函数的返回值,赋给被装饰的函数,即:
index = wrapper(index)
index = inner函数
所以,下面执行的index(),实际上就是执行的inner函数
'''
@wrapper
def index():
print('index')
index()
# 1. 装饰器之装饰无参函数 >>> >>> def bar(func): ... def wrapper(): ... return "Good " + func() ... return wrapper ... >>> def foo(func): ... def wrapper(): ... return "evening " + func() ... return wrapper ... >>> @bar ... @foo ... def hello(): ... return 'standby' ... >>> hello() 'Good evening standby' >>>
# 2. 装饰器之装饰有参函数
>>>
>>> def bar(func):
... def wrapper(name):
... return "Good " + func(name)
... return wrapper
...
>>> def foo(func):
... def wrapper(name):
... return "evening " + func(name)
... return wrapper
...
>>> @bar
... @foo
... def hello(name):
... return name
...
>>> hello('standby')
'Good evening standby'
>>>
# 3. 装饰器之装饰参数数量不确定的函数
>>>
>>> def bar(func):
... def wrapper(*args, **kwargs):
... return "Good " + func(*args, **kwargs)
... return wrapper
...
>>> def foo(func):
... def wrapper(*args, **kwargs):
... return "evening " + func(*args, **kwargs)
... return wrapper
...
>>> @bar
... @foo
... def hello(name):
... return name
...
>>> @bar
... @foo
... def hi(firstname,lastname):
... return firstname+lastname
...
>>> hello('standby')
'Good evening standby'
>>> hi('liu','lixin')
'Good evening liulixin'
>>>
带参装饰器参考:http://www.cnblogs.com/standby/p/6910613.html
如果你的装饰器如果带参数呢?
那么你就需要在原来的装饰器上再包一层,用于接收这些参数。
这些参数(私货)传递到内层的装饰器里后,闭包就形成了。
所以说当你的装饰器需要自定义参数时,一般都会形成闭包。(类装饰器例外)
# 不带参数的装饰器
>>>
>>> def debug(func):
... def wrapper(*args, **kwargs):
... print("[DEBUG]: enter {}()".format(func.__name__))
... return func(*args, **kwargs)
... return wrapper
...
>>> @debug
... def say(something):
... print("say {}!".format(something))
...
>>> say('goodbye')
[DEBUG]: enter say()
say goodbye!
>>>
# 4. 装饰器之带参数的装饰器
>>>
>>> def logger(level):
... def debug(func):
... def wrapper(*args, **kwargs):
... print("[{level}]: enter {func}()".format(level=level, func=func.__name__))
... return func(*args, **kwargs)
... return wrapper
... return debug
...
>>> @logger(level='INFO')
... def say(something):
... print("hello {}!".format(something))
...
>>> @logger(level='DEBUG')
... def talk(somebody):
... print("talk to {}".format(somebody))
...
>>> say('evening')
[INFO]: enter say()
hello evening!
>>> talk('eric')
[DEBUG]: enter talk()
talk to eric
>>>
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的。
>>>
>>> class Test():
... def __call__(self):
... print('call me!')
...
>>> obj = Test()
>>> obj()
call me!
>>>
# 5. 装饰器之不带参数的类装饰器
>>>
>>> class logging(object):
... def __init__(self, func):
... self.func = func
... def __call__(self, *args, **kwargs):
... print("[DEBUG]: enter function {func}()".format(func=self.func.__name__))
... return self.func(*args, **kwargs)
...
>>> @logging
... def say(something):
... print("say {}!".format(something))
...
>>> say('evening')
[DEBUG]: enter function say()
say evening!
>>>
>>>
带参的类装饰器在构造函数里接受的就不是一个函数,而是传入的参数。
通过类把这些参数保存起来。然后在重载__call__方法是就需要接受一个函数并返回一个函数。
# 6. 装饰器之带参数的类装饰器
>>>
>>> class logging(object):
... def __init__(self, level='INFO'):
... self.level = level
... def __call__(self, func): # 接受函数
... def wrapper(*args, **kwargs):
... print("[{level}]: enter function {func}()".format(level=self.level, func=func.__name__))
... func(*args, **kwargs)
... return wrapper #返回函数
...
>>> @logging(level='INFO')
... def say(something):
... print("say {}!".format(something))
...
>>> say('evening')
[INFO]: enter function say()
say evening!
>>>
2018-06-21 补充
不带参数的类装饰器
class logging(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("[DEBUG]: enter function {func}()".format(func=self.func.__name__))
return self.func(*args, **kwargs)
@logging
def say(something):
print("say {}!".format(something))
say('hi')
'''
- logging(say)
- say = logging(say)
- say():
- say = logging(say) == obj
- say() == obj() == __call__()
'''
带参数的类装饰器
class logging(object):
def __init__(self, level):
self.level = level
def __call__(self, func):
def wrapper(*args,**kwargs):
print("[{level}]: enter function {func}()".format(level=self.level,func=func.__name__))
return func(*args, **kwargs)
return wrapper
@logging(level='INFO')
def say(something):
print("say {}!".format(something))
say('hi')
'''
- logging(level='debug') return obj
@obj
def say(something):
print("say {}!".format(something))
- say == obj(say) == __call__(say) == wrapper
- say() == obj() == wrapper()
'''
出处:http://www.cnblogs.com/standby/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

浙公网安备 33010602011771号