2011-05-29 114 views
8

我写了这个代码:蟒蛇noobie范围界定问题

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

试图测试,如果蟒蛇是词汇或动态范围。我的想法是,

y = counter() 
y() 

要么打印0或1,这将告诉我如何python作用域。但是,调用y会抛出异常,表示x未定义。我对Python的工作原理似乎有些根本性的缺陷。

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

+0

可能重复http://stackoverflow.com/questions/2009402/read -write-python-closures) – 2011-05-29 23:14:10

回答

2

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

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

10

the docs

的Python的一个特别之处就是 - 如果没有 全球声明生效 - 分配到的名字总是进入 最里面的范围。作业 不复制数据 - 它们仅将名称 绑定到对象。

所以,当Python解析

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

它看到分配x += 1,因此决定x必须在最里面的范围。稍后,您通过y()调用temp(...) - (顺便提一句,self应该从temp的定义中省略,否则y()应该提供一个参数) - 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 
6

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

(全局命名空间是特别的好,因为它实际上是模块全局命名空间和内置命名空间,这是藏在builtins__builtins__模块中的两个。)

在你的情况,你有三个x变量:一个在模块(全局)范围内,一个在counter函数中,另一个在temp函数中 - 因为+=也是一个赋值语句。由于分配给名称这一简单事实使其对函数本地化,因此您的+=语句将尝试使用尚未分配的局部变量,并且会引发UnboundLocalError。

如果用于所有这三个x引用来引用全局变量的,你需要做的global x同时在countertemp功能。在Python 3.x(但不是2.x)中,有一个nonlocal声明,就像global一样,您可以使用temptemp分配给counter中的变量,但保留单独的全局x

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关键字覆盖默认作用域规则。

尽管变量名称是静态解析的,但变量是动态解析的。变量的值来自最近一次定义或在变量被访问时删除该变量。值在变量所在的函数闭包中查找。

+0

无论Python人员怎么称呼它,你忘记在Python函数中初始化的变量都有动态范围。这是Python更尖锐的瑕疵之一(不一定它是动态范围,但它有时是*动态范围)。 – 2013-02-26 19:19:12

+0

@ChrisPacejo:什么意思是“动态范围”(而不是词法范围)? – user1071847 2017-06-20 13:13:17

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 
[读/写的Python闭包(指