关于Python变量范围的困惑

时间:2011-03-03 17:16:32

标签: python scope

我遇到了一些令我困惑的代码。这是一个显示这个的最小例子:

# of course, the ... are not part of the actual code
some_var = {"key1":"value1" ... "keyN":"valueN"}

def some_func():
   v = some_var["key1"]

代码有效,但我可以直接访问some_var的事实让我困惑。我最后一次编写一些Python代码时,我记得要像这样编写some_func

def some_func():
   global some_var
   v = some_var["key1"]

我在Windows 7 PC上使用Python 2.7.1。 2.7版本中有什么变化允许这个吗?

6 个答案:

答案 0 :(得分:6)

不,您无法在本地范围内重新分配some_var。请考虑以下事项:

some_var = {}
def some_func():
    # some_var[5] = 6
    some_var = {1:2}
    some_var[3] = 4
some_func()
print (repr(some_var)) # {}

您会看到some_func中的作业实际上创建了一个局部变量,该变量会影响全局变量。因此,取消注释该行将导致UnboundLocalError - 您无法在定义之前访问变量。

答案 1 :(得分:3)

如果要为该变量分配新值,则只需使用global

Nested scope was introduced in Python 2.1(和enabled by default in Python 2.2)(强调我的):

  

简单地说,当给定的变量名称未在函数中指定值时(通过作业,或defclass或{{1} }语句),对变量的引用将在在封闭范围的本地命名空间中查找。可以在PEP中找到有关规则的更详细说明以及对实施的剖析。

答案 2 :(得分:3)

使用(例如在表达式中调用或使用)来自外部作用域的名称和赋值它之间存在差异(并且在分配裸变量和指定变量指向的对象成员之间存在差异 - { {1}}和x.y = ...计为方法调用!)。

如果要重新分配变量,则只需要声明变量来自外部作用域。在Python 2中,您只能使用全局变量(通过x[...] = ...),在Python 3中,您可以使用global var为abritarily嵌套作用域(例如闭包)执行此操作。

使用示例中的非本地变量不需要nonlocal var。但是,只要您在该范围内的任何位置分配变量(具有上述分配定义),就可以在您使用它的行之后添加一行global,然后您将获得{ {1}}。有关细节的详细信息,请参阅文档。

答案 3 :(得分:2)

如果您打算分配给变量,则只需要使用global,因为读取变量不需要。这种差异并不是随意的,尽管乍一看似乎是这样。

当读取值时,解释器只能查找名为some_var的局部变量,如果找不到它,则会查找该名称的全局变量。这些都是简单直接的语义。

为变量赋值时,解释器需要知道您是要分配给局部变量some_var还是全局变量。 解释器假设在函数内调用时some_var = 2分配给局部变量,这是有意义的,因为这是最常见的情况。对于您希望从函数中分配全局变量的相对罕见的时间,您可以使用全局修饰符global some_var = 2

答案 4 :(得分:1)

这取决于函数中变量的用法 Python variable scope error

答案 5 :(得分:1)

为名称赋值会使名称为local,除非名称显式声明为global。

a = 12

def foo():
   a = 42
   print a   # uses local

foo()
>>> 42

def foo():
   global a
   a = 42

foo()
print a
>>> 42

如果未指定名称,则为全局。

a = 12

def foo():
   print a   # uses global

foo()
>>> 12

简而言之,如果要分配名称,则只需要明确声明全局名称。如果您只是阅读它,您可以随意使用它。但是,如果您分配给变量,除非您将其声明为全局,否则它将被视为该函数的本地。

b = 5

def foo():
   print b
   b = 7

foo()
>>> ???

由于b已分配给foo()且未声明为全局,因此Python在编译时决定 b是本地名称。因此,b是整个功能中的本地名称​​, 包括,在分配前的print语句 < / p>

因此,print语句会给您一个错误,因为尚未定义本地名称b