2025年3月25日 星期二 甲辰(龙)年 月廿四 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

python的闭包和装饰器

时间:11-13来源:作者:点击数:35
  • 闭包(closure)
  • >>> def F(name):
  • def f():
  • print name
  • return f
  • >>> f1=F('f1')
  • >>> f2=F('f2')
  • >>> f1()
  • f1
  • >>> f2()
  • f2

F 是一个返回函数的函数。对于 F 产生的函数 f1、f2,其依赖于F的本地变量 name,但是在F执行结束后,函数 f1、f2 依然可以访问 name,而且相互独立。就像在返回内层函数时,把产生它的环境一块儿打包返回了,这种语法现象叫闭包(closure)。

在《Programming in Lua 2nd》书籍中展示了闭包的各种绚丽的用法,感觉 lua 把闭包和元编程用到极致了。即使不打算用 lua,也强烈推荐读一读,对了解 python 和其他动态语言的本质和实现或有帮助。

在 lua 中有这样一种写法:

  • >> function gen_ins(init)
  • .. local i=init-1
  • .. return function() i=i+1; return i end
  • .. end
  • >> ins5=gen_ins(5)
  • >> ins2=gen_ins(2)
  • >> ins5()
  • => 5
  • >> ins2()
  • => 2

但是这在 python 中却行不通:

  • >>> def gen_ins(init):
  • i=init-1
  • def ins():
  • i=i+1
  • return i
  • return ins
  • >>> ins0=gen_ins(0)
  • >>> ins0()
  • Traceback (most recent call last):
  • File "<pyshell#111>", line 1, in <module>
  • ins0()
  • File "<pyshell#108>", line 4, in ins
  • i=i+1
  • UnboundLocalError: local variable 'i' referenced before assignment

这就是所谓的 python 不支持真闭包,python 中对外层变量不能赋值。一般在 python 的函数中,变量的查找顺序是局部符号表,全局符号表,内置符号表。我们知道,全局变量可以被引用,但是要赋值的话,必须用 global 声明一下,否则其实是新建了一个本地变量。从python解释器的角度看,“i=i+1”中 i 既然被赋值,就应该当做本地变量,所以就会发生上述 UnboundLocalError 错误。i 也并不是全局变量,所以在 ins 中用 global 声明了 i 则会找不到全局变量。按照 PEP3104 来看,这是一个由来已久的问题。

PEP3104 中提到一种解决办法如下:

  • >>> def gen_ins(init):
  • class T: pass
  • t=T()
  • t.i=init-1
  • def ins():
  • t.i=t.i+1
  • return t.i
  • return ins
  • >>> ins0=gen_ins(0)
  • >>> ins0()
  • 0

称为"wrapping it in a mutable object",看起来很诡异。因为Guido觉得不能改动关键字 global 的含义,所以在 python 3 中引入了 nonlocal 关键字,总算解决了这个问题。


  • 装饰器(decorators)

前面的闭包算是装饰器的引子了。装饰器有两种形式:

  • @A
  • def foo():
  • pass

相当于:

  • def foo():
  • pass
  • foo = A(foo)

而:

  • @A(arg)
  • def foo():
  • pass

则相当于:

  • def foo():
  • pass
  • foo = A(arg)(foo)

可以看出第一种的装饰器是个返回函数的函数,第二种的装饰器是个返回 返回函数的函数 的函数。两种装饰器的简单示例:

  • def A(func):
  • def newfunc(*args, **argkw):
  • print 'A'
  • return func(*args, **argkw)
  • return newfunc
  • def A(arg):
  • def _A(func):
  • def newfunc(*args, **argkw):
  • print arg
  • return func(*args, **argkw)
  • return newfunc
  • return _A

装饰器中的嵌套定义的函数就涉及到 python 中闭包的问题。

装饰器可以做很多事,比如在原函数调用前检查参数,或者检查登陆状态,调用后记录日志什么的。python 中默认有 staticmethod 和 classmethod 两个装饰器。

staticmethod用来声明类的静态方法,这样调用时就不会传入实例对象(self):

  • >>> class T:
  • name = 'T'
  • @staticmethod
  • def getname():
  • print T.name
  • >>> T.getname()
  • T

classmethod 修饰那些直接以类对象作为参数的方法:

  • >>> class T:
  • name = 'T'
  • @classmethod
  • def getname(a_class): print a_class.name
  • >>> T.getname()
  • T

其实更”正常“的是不用装饰器:

  • >>> def getname(a_class): print a_class.name
  • >>> class T:
  • name = 'T'
  • getname = classmethod(getname)
  • >>> T.getname()
  • T

 

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门