Python 2.7 闭包的局限
Python 2.7 的闭包中的自由变量(co_freevars)只读的.Python需要某些技巧来"变相修改"自由变量:
>>> def add(n): freevar=[n] def closure(): freevar[0]+=1 return freevar[0] return closure >>> add100=add(100) >>> add100() 101 >>> add100() 102
如果你这样定义,则会出错:
>>> def add(n): def closure(): n+=1 return n return closure >>> add100=add(100) >>> add100() Traceback (most recent call last): File "<pyshell#15>", line 1, in <module> add100() File "<pyshell#13>", line 3, in closure n+=1 UnboundLocalError: local variable 'n' referenced before assignment >>>
原因在于Python的数字,字符串是"不可变类型".列表是"可变类型".例如:
>>> a=10 >>> id(a) 19519444 >>> a=a+3 >>> id(a) 19519408 >>> a=[] >>> id(a) 35054536 >>> a.append(1) >>> id(a) 35054536 >>> a [1] >>>
而scheme的数字对象就是可变的,而且闭包中的自由变量是可以修改的:
> (define (add n) (lambda () (set! n (+ 1 n)) n)) > (define add100 (add 100)) > (add100) 101 > (add100) 102
其实Python的n+=1有些类似于scheme的 (set! n (+ 1 n)).只不过Python中,n已经指向另外一个新对象了.而sheme,n指向的对象没有发生改变.
更多例证:
(define (add n) (lambda () (set! n (+ 1 n)) n)) (define (addx n) (lambda () (let ((n 200)) (set! n (+ 1 n)) n))) (define add5 (add 5)) (define addx5 (addx 5)) (add5) (addx5) (add5) (addx5)
结果:
6 201 7 201 >
又如:
>>> def foo(): m = [3] def bar(s): s=m[0]+s n=2 r=5 def eri(): print 2 for item in dir(bar.func_code): if isinstance(getattr(bar.func_code,item),tuple):
print "%s : %s"%(item,getattr(bar.func_code,item)) >>> foo() co_cellvars : () co_consts : (None, 0, 2, 5, <code object eri at 01ED8020, file "<pyshell#16>", line 7>) co_freevars : ('m',) co_names : () co_varnames : ('s', 'n', 'r', 'eri') >>> def foo(): m = [3] def bar(s): s=m[0]+s n=2 r=5 def eri(): print n for item in dir(bar.func_code): if isinstance(getattr(bar.func_code,item),tuple):
print "%s : %s"%(item,getattr(bar.func_code,item)) >>> foo() co_cellvars : ('n',) co_consts : (None, 0, 2, 5, <code object eri at 01FE3B18, file "<pyshell#19>", line 7>) co_freevars : ('m',) co_names : () co_varnames : ('s', 'r', 'eri') >>>
co_varnames 本地变量
co_freevars 自由变量(闭包体现)
co_cellvars (被子函数引用的变量,不知叫啥合适)