Python中

时间:2018-06-02 03:11:06

标签: python python-3.x list mutability

我是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的版本运行正常吗?作者表示“这会利用列表的可变性,并依赖于就地对象不将名称归类为本地的事实。”

我不确定这意味着什么。有人可以帮帮我吗?感谢您对我的任何帮助。

3 个答案:

答案 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会自动认为stateinteger,因为您正在尝试添加state。虽然state[0]是一个列表,但除非append是其中的一个元素,否则您无法添加到该列表中 - 这不是您的情况。 state工作而不是state[0]的原因是因为statehttps://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之类的操作之前和之后将具有相同的内存位置。

这个非常基本的解释有很多含义需要一些时间来解释。例如,变量不能“指向”其他变量,而是仍然指向底层对象。

因此,列表可以表现出奇怪的"行为(另一个常见的,相关的混淆围绕将默认参数值设置为随后在函数中修改的列表),但也可以按照示例显示的方式利用。