在内部函数内部分配外部变量的python闭包

nem*_*emo 15 python closures scope python-2.x

我有这段代码:

#!/usr/bin/env python

def get_match():
  cache=[]
  def match(v):
    if cache:
      return cache
    cache=[v]
    return cache
  return match
m = get_match()
m(1)
Run Code Online (Sandbox Code Playgroud)

如果我运行它,它说:

UnboundLocalError: local variable 'cache' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

但如果我这样做:

#!/usr/bin/env python

def get():
  y = 1
  def m(v):
    return y + v
  return m

a=get()
a(1)
Run Code Online (Sandbox Code Playgroud)

它运行.

列表中有什么东西吗?或者我的代码组织错了?

l4m*_*mpi 26

问题是变量cache不在函数匹配的范围内.如果您只想在第二个示例中阅读它,这不是问题,但如果您要分配它,python会将其解释为局部变量.如果你正在使用python 3,你可以使用nonlocal关键字来解决这个问题 - 对于python 2,不幸的是没有简单的解决方法.

def f():
    v = 0

    def x():
        return v    #works because v is read from the outer scope

    def y():
        if v == 0:  #fails because the variable v is assigned to below
            v = 1

    #for python3:
    def z():
        nonlocal v  #tell python to search for v in the surrounding scope(s)
        if v == 0:
            v = 1   #works because you declared the variable as nonlocal
Run Code Online (Sandbox Code Playgroud)

全局变量的问题有些相同 - global每次分配给全局变量时都需要使用,但不能用于读取全局变量.

背后原因是什么的简短解释:Python解释器编译所有功能于类型的特殊对象function.在编译期间,它会检查函数创建的所有局部变量(用于垃圾收集等).这些变量名称保存在函数对象中.因为"影子"外部范围变量(创建具有相同名称的变量)是完全合法的,所以分配给的任何变量以及未明确声明为global(或nonlocal在python3中)的变量都被假定为局部变量.

执行该函数时,解释器必须查找它遇到的每个变量引用.如果在编译期间发现变量是本地变量,则在函数f_locals字典中搜索它.如果尚未分配,则会引发您遇到的异常.如果变量未在函数范围中赋值,因此不是其本地的一部分,则会在周围的范围中查找 - 如果在那里找不到,则会引发类似的异常.

  • @Brandon这是一个简单但有效的性能优化 - 局部变量将存储为PyFrameObject数组中PyObject的指针; 获取或设置局部变量就像索引数组一样简单.外部范围的变量必须通过更复杂的过程来查找; 有关详细信息,请参阅[来源](http://hg.python.org/cpython/file/3fe2fd4ffa32/Python/ceval.c#l1353)(本地区为LOAD_FAST,否则为LOAD_NAME/GLOBAL/DEREF).而且我认为当你想要覆盖全局时必须声明它是好的:比如Zen状态,`explicit> implicit`. (2认同)

Mih*_*tan 7

访问变量与分配变量不同.

您对全局变量有类似的情况.您可以在任何函数中访问它们,但如果您尝试在没有该global语句的情况下分配它们,它将在本地上下文中重新声明它.

不幸的是,对于本地函数,没有相应的global声明,但您可以通过替换来绕过重新声明

cache=[v]
Run Code Online (Sandbox Code Playgroud)

有:

cache[:] = [v]
Run Code Online (Sandbox Code Playgroud)