您当前的位置:首页 > 计算机 > 编程开发 > Python

python的闭包和装饰器

时间:11-13来源:作者:点击数:
  • 闭包(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

 

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