python noobie范围问题

时间:2011-05-29 23:11:50

标签: python scoping

我写了这段代码:

x = 0
def counter():
 x = 1
 def temp(self):
  print x
  x += 1
 return temp

尝试测试python是词法还是动态范围。我的想法是

y = counter()
y()

应该打印0或1,这会告诉我python是如何作用域的。但是,调用y会抛出一个异常,说x未定义。在理解Python如何工作的过程中,似乎存在一些根本性的缺陷。

有人可以解释这是如何工作的吗?是的,我知道可以使用对象轻松完成。我正试图探索在不使用对象的情况下给出函数状态的想法。我用这种方式编写代码是因为上面翻译成像SCI这样的词汇范围的语言肯定会有效。

5 个答案:

答案 0 :(得分:10)

来自the docs

  

Python的一个特殊怪癖是 - 如果   没有全球声明生效 -   名称的赋值总是进入   最里面的范围。作业呢   不复制数据 - 它们只是绑定名称   对象。

所以,当Python解析

 def temp(self):
  print x
  x += 1

它会看到作业x += 1,因此决定x必须位于最里面的范围内。稍后您通过temp(...)致电y() - (顺便说一下,self的定义中应忽略tempy()应该提供一个参数) - Python遇到print x语句并发现x尚未在本地(最里面)范围内定义。因此错误,

UnboundLocalError: local variable 'x' referenced before assignment

如果您声明

 def temp(self):
     global x

然后Python将在全局范围内查找x(其中x=0)。 在Python2中,没有办法告诉Python在扩展范围(x)中查找x=1。但是在Python3中可以通过声明来实现

 def temp(self):
     nonlocal x

答案 1 :(得分:6)

Python有两个范围(实际上是三个)以及嵌套本地范围的特殊规则。对于模块级名称,两个范围是 global ,对于函数中的任何内容, local 。您在函数中分配的任何内容都将自动为本地,除非您使用该函数中的global语句声明其他内容。如果使用未分配给函数中任何位置的名称,则它不是本地名称; Python将(词法)搜索嵌套函数,以查看它们是否具有该名称作为本地名称。如果没有嵌套函数或者其中任何一个名称不是本地函数,则假定该名称是全局的。

(全局命名空间也很特殊,因为它实际上既是模块全局命名空间又是内置命名空间,它隐藏在builtins__builtins__模块中。)

在您的情况下,您有三个 x变量:一个在模块(全局)范围内,一个在counter函数中,一个在{{1}中函数 - 因为temp也是赋值语句。因为分配给名称的这一事实使得它成为函数的本地,所以+=语句将尝试使用尚未分配的局部变量,这将引发UnboundLocalError。

如果您打算将所有这三个+=引用用于引用全局变量,则需要在xglobal x中执行counter功能。在Python 3.x(但不是2.x)中,有一个temp声明与nonlocal非常相似,您可以使用它globaltemp中的变量赋值,但仅保留全球counter

答案 2 :(得分:2)

Python中的闭包不可写,因此您无法以这种方式编写代码。如果你只读取函数内部的变量,你应该是好的。在Python 3中,您可以使用nonlocal关键字来获取所需的行为。

如果您使用的是Python 2.5或更高版本,您还可以使用yield关键字将上述代码编写为生成器。

答案 3 :(得分:2)

Python文档详细解答了这个问题:http://docs.python.org/reference/executionmodel.html#naming-and-binding

总之,Python有静态作用域规则。如果函数f定义或删除变量名,则变量名引用函数f的闭包中的变量。如果函数f仅使用变量名(无定义或删除),则名称引用f的父作用域中名称的含义。继续前进到父作用域,直到找到定义或达到全局作用域。例如:

def f1(x):
  def g(y):
    z.foo = y # assigns global z
  g(1)

def f2(x):
  def g(y):
    x.foo = y # assigns f2's variable, because f2 defines x 
  g(1)

def f3(x):
  def g(y):
    x = C()
    x.foo = y # assigns g's variable, because g defines x
  g(1)

global关键字和(在Python 3中)nonlocal关键字覆盖默认的范围规则。

当静态解析变量名称时,动态解析变量。变量的值来自访问变量时该变量的最新定义或删除。在变量所在的函数闭包中查找值。

答案 4 :(得分:1)

感谢您的所有回复。万一有人关心,我有点想出一个解决这个问题的方法。我通过创建一个“scoper”函数来建立一个计数器变量。

>>> def gc():
...  def scoper():
...   scoper.s = 0
...   def rtn():
...    scoper.s += 1
...    return scoper.s
...   return rtn
...  return scoper()

以上允许我这样做,模仿正确的闭包:

>>> a = gc()
>>> a()
1
>>> a()
2
>>> a()
3
>>> b = gc()
>>> b()
1
>>> a()
4
>>> b()
2
相关问题