【问题标题】:Modifying a list iterator in Python not allowed?不允许在 Python 中修改列表迭代器?
【发布时间】:2012-03-13 22:30:43
【问题描述】:

简单示例:

myList = [1, 2, 3, 4, 5]
for obj in myList:
  obj += 1
print myList

打印

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

同时:

myList = [1, 2, 3, 4, 5]
for index in range(0,len(myList)):
  myList[index] += 1
print myList

打印

[1, 2, 3, 4, 5]
[2, 3, 4, 5, 6]

结论:

  1. 列表可以使用全局列表访问就地修改列表可以
  2. 列表项不能使用迭代器对象就地修改

我能找到的所有示例代码都使用全局列表访问器来就地修改列表。 修改列表迭代器就这么邪恶吗?

【问题讨论】:

  • 按原样,此代码将生成 NameError,因为未定义 a
  • 您的代码示例很糟糕。最早的印刷品从何而来?您的打印“a”不存在并且有两个输出。
  • 对不起,'a' 显然应该是 'myList'。已在帖子中修复。

标签: python list iterator


【解决方案1】:

在第一个示例中,整数被复制到 obj 中,obj 增加 1。 列表没有改变。

如果你要使用一个类实例并对其执行操作,它就会被改变。

【讨论】:

    【解决方案2】:

    你很困惑。考虑你的第一个 sn-p:

    myList = [1, 2, 3, 4, 5]
    for obj in myList:
      obj += 1
    print a
    

    obj 不是某种指向列表的神奇指针。它是一个变量,它包含对恰好也在 myList 中的对象的引用。 obj += 1 具有增加存储在obj 中的值的效果。然后,您的代码不会对该值执行任何操作。

    需要明确:此代码示例中没有副本。 obj 是一个变量,它保存列表中的一个对象。就是这样。

    【讨论】:

    • 如果obj 是 C++ 中的迭代器,则列表对象将被更新。但显然,obj 是一个副本,每次迭代后都会忽略更改。显然,Python 是后者,这是我的问题。
    • @RobW:在我之后重复:此代码示例中没有副本。 obj 是一个变量,它保存了列表中的一个对象。就是这样。
    【解决方案3】:

    obj += 1 没有按照您的预期执行的原因是该语句没有就地修改obj。相反,它计算新值,并重新绑定变量obj 以指向新值。这意味着列表的内容保持不变。

    一般来说,可以在使用for obj in myList 迭代列表时修改列表。例如:

    myList = [[1], [2], [3], [4], [5]]
    for obj in myList:
      obj[0] += 1
    print(myList)
    

    打印出来:

    [[2], [3], [4], [5], [6]]
    

    此示例与您的第一个示例之间的区别在于,此处的列表包含 可变 对象,并且代码会就地修改这些对象。

    请注意,也可以使用列表推导式编写循环:

    myList = [val+1 for val in myList]
    

    【讨论】:

    • 感谢您的回答。我已经得出结论,obj 迭代器不是 C++ 中的迭代器,修改对象不会更改列表。您所说的是obj 再次绑定到将在下一次迭代中丢失的新对象。清除!
    • 很好的答案!这与接受的答案所谈论的“浅拷贝”无关。
    【解决方案4】:

    for obj in myList: 中,在每次迭代中,objmyList 中元素的(浅)副本。所以obj 上的更改对myList 的元素没有任何影响。

    Perl for my $obj (@myList) {}不同

    【讨论】:

    • 没错,我现在明白了。 obj 不是 C++ 中的迭代器,它基本上是对象的副本,在下一次迭代中将被忽略。因此,只能使用全局/直接列表访问进行修改。
    • @RobW, obj 不是副本,这个答案具有误导性。 obj - 就像 Python 中的所有变量一样 - 更像是 C/C++ 中的指针。查看my answer的编辑。
    • Ade YU,我猜你说“复制”是指“元素地址的副本”而不是“元素本身的副本”。你可以澄清一下。
    • @senderle 是的。在 Python 中,variables 只是对 objects 的引用。只复制变量,但对象。这就像obj = myList[index] 发生在每次迭代的最开始。所以obj += 1 不会影响myList[index]。谢谢你。我会改进我的回答陈述。
    • 如果是列表列表,可能不是浅拷贝
    【解决方案5】:

    允许修改列表。您的代码示例arbove非常乱码......

    myList = [1, 2, 3, 4, 5]
    for index in range(0,len(myList)):
       myList[index] += 1
     print myList
    

    这行得通。

    【讨论】:

    • 这确实是我发布的,没有回答任何问题。
    【解决方案6】:

    我认为您误解了“迭代器对象”是什么。 for 循环不是迭代器对象。出于所有意图和目的,这样的 for 循环:

    myList = [0, 1, 2, 3, 4]
    for x in myList:
        print x
    

    这样做(但更有效,更简洁):

    i = 0
    while i < len(myList)
        x = myList[i]
        print x
        i += 1
    

    所以你看,对x 所做的任何更改都会在下一个循环开始时丢失,因为x 的值会被列表中下一项的值覆盖。

    正如其他人所观察到的,可以在迭代列表时更改列表的值。 (但不要改变它的长度!这就是你遇到麻烦的地方。)一种优雅的方法如下:

    for i, x in enumerate(myList):
        myList[i] = some_func(x)
    

    更新:了解 没有复制 在 for 循环中进行也很重要。在上面的例子中,ix——就像 Python 中的所有变量一样——更像是 C/C++ 中的 指针。随着 for 循环的进行,obj points 依次指向myList[0]myList[1] 等。和 C/C++ 指针一样,指针改变时指向的对象的属性也不会改变。但也像 C 指针一样,您可以直接修改指向的东西,因为它不是副本。在 C 中,这是通过 取消引用 指针来完成的;在 Python 中,这是通过使用可变对象来完成的。这就是NPE 的答案有效的原因。如果ix 甚至是浅拷贝,就不可能做到他所做的事情。

    您不能像更改 lists 那样直接更改 ints 的原因(如 NPE 的回答),是 ints 不可变。一旦创建了5 对象,就无法更改其值。这就是为什么在 Python 中传递指向 5 的指针是安全的——不会发生副作用,因为指向的东西是不可变的。

    【讨论】:

    • 很明显,print a 应该是 print myList,我已经解决了这个问题。感谢指向enumerate 的指针,非常有用,避免为列表索引维护自己的代码!
    • 为了更加pythonic和简洁,您可以使用[some_func(x) for x in myList]map(some_func, myList),尽管它们会复制初始列表。
    猜你喜欢
    • 2014-01-31
    • 1970-01-01
    • 1970-01-01
    • 2018-10-12
    • 2011-01-18
    • 2020-09-07
    • 1970-01-01
    • 2021-08-11
    • 2014-09-17
    相关资源
    最近更新 更多