装饰器中不同类型的变量是否有不同的范围? (蟒蛇)

时间:2015-09-20 20:39:20

标签: python scope closures python-decorators

我很难理解python-decorators中的变量范围。我在某处读到非局部变量以只读方式存储。但不知何故,词典似乎是一个例外。

def outer(f):
    def inner():
        print val
        return f()
    val =1
    return inner

def outer2(f):
    def inner2():
        val+=1
        print val
        return f()
    val =1
    return inner2

def outer3(f):
    def inner3():
        d[0]+=1
        print d
        return f()
    d ={0:0}
    return inner3

import doctest

class Test: """
>>> function = lambda : 'Function called'

>>> f1=outer(function)
>>> f1()
1
'Function called'

>>> f2=outer2(function)
>>> f2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in inner2
UnboundLocalError: local variable 'val' referenced before assignment

>>> f3=outer3(function)
>>> f3()
{0: 1}
'Function called'
"""

print (doctest.testmod())

为什么val不在f2的范围内?
为什么字典没有整数相同的问题?

提前致谢!

2 个答案:

答案 0 :(得分:0)

原因是像+=这样的操作的性质取决于左侧的目标类型,因此var += 1var[blah] += 1不同。 }。如果左侧是裸名称,则这是一个变量重新绑定。如果不是,那就不是。对于d[0] += 1+=由字典对象处理,不涉及重新绑定名称d

相关文档为here。请注意第一个项目符号点(其中结果只是“名称被绑定”)与所有其他项目符号点之间的区别,其中结果是“要求对象...”。

答案 1 :(得分:-2)

我认为&#34;只读&#34;你的意思是你不能将名称重新绑定到非局部范围内的对象(在Python 2中也是如此)。使用您的字典示例,您不会将名称重新绑定到对象 - 您正在改变字典实例 - d[0] += 1要求d对象本身检索与键0关联的值,并且然后要求对象放回添加的结果。

所以你对dict的处理与你对int的处理有着本质的不同,这就是为什么你认为字典没有像int这样的问题。这种误解经常出现,因为dicts是可变的,代码可以很容易地改变dict的内容,并且程序员很容易陷入这样的陷阱,即当他们真正重新绑定整数时,他们同样会改变整数的内容。

根据定义,整数是不可变的 - 你不能改变它们 - 所以为了改变&#34;一个整数变量,您始终必须将标识符重新绑定到另一个对象。但是,如果你反击了这个词,例如使用d = d.copy()之类的代码,你会看到同样的问题。

在Python中,变量的id是其内存地址 - 不同的地址,不同的对象。正如你在这里看到的那样,当你修改列表的元素时,你仍然拥有相同的列表对象,但是当你修改一个整数时 - 你并没有真正修改它 - 你得到了一个不同的一个:

>>> x = [0]
>>> id(x), id(x[0])
(3063883308, 138405104)
>>> x[0] += 1
>>> id(x), id(x[0])
(3063883308, 138405120)

正如BrenBarn在下面和他的回答中指出的那样,我最初误解了你的问题的全部范围,因为我专注于你最初的混淆,为什么某些东西与字典实例中的一个项目一起使用并且在使用时没有用int直接。

在函数内部,对裸变量的赋值(例如,不调用对象上的__setattr__函数)总是对局部变量进行赋值,除非它们被声明为global或(在Python 3中){{ 1}}。

nonlocal等增强分配遵循与分配相同的规则,但它们自然要求在修改之前首先存在初始值。因此,当对未声明为非局部变量或全局变量的变量进行赋值时,它必须是局部变量,并且当它不存在时(尚未被先前的语句约束)所以它可以在增强赋值表达式中使用,您将看到引发的异常。通过添加+=语句,可以使您的失败示例与Python 3一起使用:

nonlocal