我很难理解python装饰器中变量的作用域。我在某个地方读到,非局部变量存储为只读。但是字典似乎是个例外。
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的作用域中?
为什么字典和整数没有同样的问题?
提前感谢!
原因是,像+=
这样的操作的性质是由左侧目标的类型决定的,因此var += 1
与var[blah] += 1
不是同一种东西。如果左边是一个名称,这是一个变量重绑定。如果不是,那就不是。在d[0] += 1
的情况下,+=
由字典对象处理,不涉及重新绑定名称d
。
相关文档在这里。请注意,第一个项目符号点的结果是"名称被绑定",而其他所有项目符号点的结果是"对象被要求…"。
我认为"只读"的意思是你不能在非局部范围内将名称重新绑定到对象(这在Python 2中是正确的)。在你的字典示例中,你没有将名称重新绑定到对象—你正在改变字典实例—d[0] += 1
要求d
对象本身检索与键值0相关的值,然后要求对象将添加的结果放回。
所以你对字典所做的和你对整型所做的在性质上是不同的,这就是为什么你认为字典不会像整型那样遇到同样的问题。这种误解经常出现,因为字典是可变的,代码可以很容易地改变字典的内容,程序员很容易陷入这样的陷阱:当他们真正重新绑定整数时,他们认为他们同样是在改变整数的内容。
整数根据定义是不可变的——你不能改变它们——所以为了"改变"一个整数变量,你总是必须将标识符重新绑定到一个不同的对象。但是,如果您对字典进行反弹,例如使用像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中)nonlocal
。
增强赋值(如+=
)遵循与赋值相同的规则,除了它们自然要求初始值在可以修改之前首先存在。因此,当对一个未声明为非局部或全局的变量进行赋值时,它必须是一个局部变量,当它不存在(还没有被前面的语句绑定)以便可以在增广赋值表达式中使用时,将引发您看到的异常。通过添加nonlocal
语句,可以使失败的示例与Python 3一起工作:
>>> def outer2(f):
... def inner2():
... nonlocal val
... val+=1
... print(val)
... return f()
... val = 1
... return inner2
...
>>> function = lambda : 'Function called'
>>>
>>> print(outer2(function)())
2
Function called