18

我有这段代码:

#!/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)

如果我运行它,它会说:

UnboundLocalError: local variable 'cache' referenced before assignment

但如果我这样做:

#!/usr/bin/env python

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

a=get()
a(1)

它运行。

清单上有东西吗?还是我的代码组织错误?

4

4 回答 4

31

问题是变量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

global全局变量的问题有些相同 -每次分配给全局变量时都需要使用,而不是读取它。

背后原因的简短解释:python解释器将所有函数编译成一个特殊的类型对象function。在此编译期间,它检查函数创建的所有局部变量(用于垃圾收集等)。这些变量名保存在函数对象中。由于“隐藏”外部范围变量(创建具有相同名称的变量)是完全合法的,因此任何分配给且未显式声明为global(或nonlocal在 python3 中)的变量都被假定为局部变量。

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

于 2012-08-23T12:56:24.750 回答
7

Accessing a variable is different from assigning it.

You have a similar situation with global variables. You can access them in any function, but if you try to assign to it without the global statement, it will redeclare it in the local context.

Unfortunately for local functions there is no equivalent of the global statement, but you can bypass the redeclaration by replacing

cache=[v]

with:

cache[:] = [v]
于 2012-08-23T13:00:37.580 回答
3

由于 Python 看到cache=[v]- 赋值给cache,所以它把它当作局部变量。所以这个错误是相当合理的——在声明中cache使用它之前没有定义局部变量。if

你可能想把它写成:

def get_match():
  cache=[]
  def match(v):
    if cache:
      return cache
    cache.append(v)
    return cache
  return match
m = get_match()
m(1)

强烈推荐阅读:执行模型 - 命名和绑定PEP 227 - 静态嵌套范围

于 2012-08-23T12:57:57.380 回答
1

代替

cache=[]
def match(v):

def match(v,cache=[])

说明:您的代码声明cache为 的变量get_match,返回的变量对此match(v)一无所知(由于以下分配)。但是,您确实希望cache成为match' 名称空间的一部分。

我知道这样一个“恶意”用户可以重新定义缓存,但这是他们自己的错误。如果这一个问题,替代方案是:

def match(v):
     try:
         if cache:
             return cache
     except NameError:
         cache = []
     ...

(见这里

于 2012-08-23T13:03:37.880 回答