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  (被子函数引用的变量,不知叫啥合适)

 

 

posted @ 2013-10-24 14:20  LisPythoniC  阅读(750)  评论(0编辑  收藏  举报