python noobie范围界定问题

python noobie范围界定问题,python,scoping,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如何工作的理解中,似乎存在一些根本性的缺陷 有人能解释一下这是怎么回事吗?是的,我知道这可以通过使用对象轻松

我写了这段代码:

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如何工作的理解中,似乎存在一些根本性的缺陷


有人能解释一下这是怎么回事吗?是的,我知道这可以通过使用对象轻松完成。我试图探索在不使用对象的情况下赋予函数状态的想法。我以这种方式编写代码,因为将上述内容翻译成类似Scheme的词汇范围语言肯定会奏效

Python中的闭包是不可写的,因此不能用这种方式编写代码。如果您只从函数内部的变量读取,那么您应该很好。在Python3中,可以使用
非本地
关键字来获得所需的行为

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

来源:

Python的一个特殊怪癖是——如果 没有有效的全球声明—— 给名字分配的任务总是在 最里面的范围。作业 不复制数据-它们只是绑定名称 对对象进行修改

因此,当Python解析时

 def temp(self):
  print x
  x += 1
它看到赋值
x+=1
,因此决定
x
必须在最里面的范围内。当您稍后通过
y()
调用
temp(…)
时(顺便说一句,
temp
的定义中应该省略
self
,或者
y()
应该提供一个参数)——Python遇到
print x
语句,发现
x
在本地(最里面的)范围。因此错误

UnboundLocalError: local variable 'x' referenced before assignment
如果你申报

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

 def temp(self):
     nonlocal x
Python有两个作用域(实际上是三个)加上嵌套局部作用域的特殊规则。这两个作用域是全局的,用于模块级名称,而局部的,用于函数中的任何内容。在函数中指定给的任何内容都自动是局部的,除非您在该函数中用
global
语句声明它。如果您使用未指定给函数中任何位置的名称注意,它不是一个本地名称;Python将(从词汇上)搜索嵌套函数,以查看它们是否将该名称作为本地名称。如果没有嵌套函数或任何嵌套函数中的名称都不是本地名称,则假定该名称为全局名称

(全局名称空间也很特殊,因为它实际上是模块全局名称空间和内置名称空间,它们隐藏在
内置
\uuuuuuuuuuuu内置
模块中。)

在本例中,您有三个
x
变量:一个在模块中(全局)作用域,一个在
计数器
函数中,一个在
临时
函数中——因为
+=
也是一个赋值语句。因为赋值给名称的事实使其成为函数的局部变量,所以
+=
语句将尝试使用尚未赋值的局部变量,这将引发一个错误n取消绑定LocalError


如果希望所有这三个
x
引用都引用全局变量,则需要在Python 3.x(而不是2.x)中的
counter
temp
函数中执行
global x
,有一个类似于
global
nonlocal
声明,您可以使用它将
temp
赋值给
计数器中的变量,但不要使用全局
x

总之,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)
全局
关键字和(在Python 3中)非局部
关键字覆盖默认范围规则


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

感谢您的回复。以防万一不管怎样,我已经找到了解决这个问题的方法。我创建了一个“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

不管Python的人怎么称呼它,一个你忘记在Python函数中初始化的变量都有动态作用域。这是Python最明显的缺点之一(不一定是动态作用域,但有时是动态作用域)。@ChrisPacejo:“动态作用域”是什么意思(相对于词汇范围)?