我是Python的初学者,使用Mark Lutz的书来学习Python的基础知识。
以下是作者用于演示使用列表存储状态信息的示例:
def tester(start):
def nested(label):
print(label,state[0])
state[0] += 1
state = [start]
return nested
这是测试状态信息的代码:
F = tester(3)
F('sam')
F('sam')
您会看到计数器从3开始增加然后继续。本质上,上面的代码在start
中存储初始状态[state]
(在对象初始化期间传递),并在每次调用label
时递增它。
但是,我不确定为什么Python不会在nested
块中抛出错误。具体而言,[state]
是tester
的本地而非nested.
为了证明我的意思,我将state[0]
替换为state.
def tester(start):
def nested(label):
print(label,state) #Replaced state[0] with state
state += 1 #Replaced state[0] with state
print("after:",state)
state = start #Replaced state[0] with state
return nested
从技术上讲,上面的代码也应该可以正常工作,因为我所做的就是用变量替换列表。但是,PyCharm甚至不会运行此代码。我收到错误nboundLocalError: local variable 'state' referenced before assignment
有人可以解释为什么list
的版本运行正常吗?作者表示“这会利用列表的可变性,并依赖于就地对象不将名称归类为本地的事实。”
我不确定这意味着什么。有人可以帮帮我吗?感谢您对我的任何帮助。
答案 0 :(得分:2)
You should read this section of the documentation。
基本上,在两个版本中,嵌套块的范围允许它与包含块的命名空间进行交互。不同之处在于,您没有在第一个示例中重新分配state
,而是要对其进行变更。
在第二个示例中,Python知道您将在函数中稍后为该引用赋值,因此将其视为来自本地nested
命名空间的名称,而不是外部{{1命名空间。
您可以使用tester
关键字来规避此问题并使用其他命名空间中的其他引用
nonlocal
答案 1 :(得分:1)
根据我的理解,因为nested
嵌套在tester
下,它可以访问属于tester
的任何对象和变量,因为tester
是父函数在这种情况下,nested
是子函数。由于inheritance,Python不会产生错误。
关于用state[0]
替换state
,Python会自动认为state
是integer
,因为您正在尝试添加state
。虽然state[0]
是一个列表,但除非append是其中的一个元素,否则您无法添加到该列表中 - 这不是您的情况。 state
工作而不是state[0]
的原因是因为state
是https://www.donotcall.gov.au/dncrtelem/rtw/washing.cfc?wsdl
列表中的一个元素,它会向其添加0。
答案 2 :(得分:1)
它的功能是1)Python变量赋值实际上只是为内存中的底层值创建别名(指针),以及如何处理可变类型和不可变类型之间的差异; 2)一些Python"魔术"与封闭有关。问题的关键在于第一点。
要解决这个问题,请采取以下措施:
a = 3
b = 3
a和b都指向相同的底层对象:
assert hex(id(a)) == hex(id(b))
是真的。但是,设置b = 4
将导致b指向内存中的不同对象(显示int是不可变的)。
但是,列表是可变的(可修改"到位")。例如:c = [2]
在c[0] = 3
之类的操作之前和之后将具有相同的内存位置。
这个非常基本的解释有很多含义需要一些时间来解释。例如,变量不能“指向”其他变量,而是仍然指向底层对象。
因此,列表可以表现出奇怪的"行为(另一个常见的,相关的混淆围绕将默认参数值设置为随后在函数中修改的列表),但也可以按照示例显示的方式利用。