pygame实现文本转图片
用 PIL 制作 ASCII Art

python的闭包和装饰器

scturtle posted @ 2011年12月06日 21:02 in python , 5264 阅读
  • 闭包(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
isnowfy 说:
2011年12月15日 12:41

啥叫更”正常“的是不用装饰器,装饰器不过是打字少的一种便捷方式吧。。。

Avatar_small
scturtle 说:
2011年12月15日 18:14

getname如果多个类共用的话,用装饰器反而会需要在每个类里声明一次吧,classmethod看起来是就是用来类间共享方法的,它的装饰器用法是否显得有点儿多余?

AP SSC fa 3 Model Pa 说:
2022年9月09日 02:30

Formative Assessment means not only an examination, it includes various aspects such as Examination in completed lessons, Reflections, Project work done on the allotted topic and Self also Prepared notes etc. AP SSC fa 3 Model Paper Candidates of Telugu Medium, English Medium & Urdu Medium of the AP State can download the AP 10th Class FA 4 Model Paper 2023 Pdf with answers for the regular exams conducted by the Directorate of Government Examinations, Andhra Pradesh.

google maps verlauf 说:
2023年7月23日 23:07

Google Maps sind grafische Karten, auf die über einen Computerbrowser zugegriffen werden kann. Der Google Maps-Dienst wurde ursprünglich entwickelt, um Wegbeschreibungen bereitzustellen, und aus diesem Grund wird Google Maps am häufigsten verwendet. google maps verlauf löschen Wenn Sie Google Maps häufig verwenden, haben Sie sicherlich eine lange Liste von Suchanfragen in dieser Maps-Aktivität. Nachdem Sie auf das Suchfeld von Google Maps getippt haben, wird eine Liste Ihrer letzten Suchanfragen angezeigt.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter