python-装饰器与闭包函数

闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。

如果在一个内嵌函数里,对在外部函数内(但不是在全局作用域)的变量进行引用,那么内嵌函数就被认为是闭包(closure)

定义在外部函数内但由内部函数引用或者使用的变量称为自由变量

总结一下,创建一个闭包必须满足以下几点:

  • 1. 必须有一个内嵌函数
  • 2. 内嵌函数必须引用外部函数中的变量
  • 3. 外部函数的返回值必须是内嵌函数

1.闭包使用示例

先看一个闭包的例子:

In [10]: def func(name): ...: def in_func(age): ...: print 'name:',name,'age:',age ...: return in_func ...: In [11]: demo = func('feiyu') In [12]: demo(19) name: feiyu age: 19

这里当调用 func 的时候就产生了一个闭包——in_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。

python 的函数内,可以直接引用外部变量,但不能改写外部变量,因此如果在闭包中直接改写父函数的变量,就会发生错误。看以下示例:

实现一个计数闭包的例子:

def counter(start=0):
    count = [start] def incr(): count[0] += 1 return count return incr a = counter() print 'a:',a In [32]: def counter(start=0): ...: count = start ...: def incr(): ...: count += 1 ...: return count ...: return incr ...: In [33]: a = counter() In [35]: a() #此处会报错 UnboundLocalError: local variable 'count' referenced before assignment

应该像下面这样使用:

In [36]: def counter(start=0):
    ...:     count = [start]
    ...:     def incr():
    ...: count[0] += 1 ...: return count ...: return incr ...: In [37]: count = counter(5) In [38]: for i in range(10): ...: print count(), ...: [6] [7] [8] [9] [10] [11] [12] [13] [14] [15]

2.使用闭包的陷阱

In [1]: def create():
   ...:     return [lambda x:i*x for i in range(5)] #推导式生成一个匿名函数的列表 ...: In [2]: create() Out[2]: [<function __main__.<lambda>>, <function __main__.<lambda>>, <function __main__.<lambda>>, <function __main__.<lambda>>, <function __main__.<lambda>>] In [4]: for mul in create(): ...: print mul(2) ...: 8 8 8 8 8

结果是不是很奇怪,这算是闭包使用中的一个陷阱吧!来看看为什么?

在上面的代码当中,函数create返回一个list里面保存了4个函数变量,这4个函数都共同的引用了循环变量i, 也就是说它们共享着同一个变量ii是会改变的,当函数调用时,循环变量i已经是等于4了,因此4个函数返回的都是8。如果,需要在闭包使用循环变量的值的话,把循环变量作为闭包的默认参数或者是通过偏函数来实现。实现的原理也很简单,就是当把循环变量当参数传入函数时,会申请新的内存。示例代码如下:

In [5]: def create():
   ...:         return [lambda x,i=i:i*x for i in range(5)] ...: In [7]: for mul in create(): ...: print mul(2) ...: 0 2 4 6 8

3,闭包与装饰器

装饰器就是一种的闭包的应用,只不过其传递的是函数:

def addb(func): def wrapper(): return '<b>' + func() + '</b>' return wrapper def addli(func): def wrapper(): return '<li>' + func() + '</li>' return wrapper @addb # 等同于 demo = addb(addli(demo)) @addli # 等同于 demo = addli(demo) def demo(): return 'hello world' print demo() # 执行的是 addb(addku(demo))

在执行时,首先将demo函数传递给addli进行装饰,然后将装饰后的函数传递给addb进行装饰。所以最后返回的结果是:

<b><li>hello world</li></b>

4.装饰器中的陷阱

当你写了一个装饰器作用在某个函数上,这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都会丢失。

def out_func(func): def wrapper(): func() return wrapper @out_func def demo(): """ this is a demo. """ print 'hello world.' if __name__ == '__main__': demo() print "__name__:",demo.__name__ print "__doc__:",demo.__doc__

看结果:

hello world.
__name__: wrapper
__doc__: None

函数名字和文档字符串都变成了闭包的信息。好在可以使用 functools 库中的 @wraps 装饰器来注解底层包装函数。

from functools import wraps

def out_func(func):  @wraps(func) def wrapper(): func() return wrapper

 

转载于:https://www.cnblogs.com/yileye/articles/7247724.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值