【问题标题】:Modify a list while iterating迭代时修改列表
【发布时间】:2017-12-05 11:20:12
【问题描述】:

我知道您不应该在迭代列表时添加/删除项目。但是,如果我不更改列表长度,我可以修改我正在迭代的列表中的项目吗?

class Car(object):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return type(self).__name__ + "_" + self.name

my_cars = [Car("Ferrari"), Car("Mercedes"), Car("BMW")]
print(my_cars)  # [Car_Ferrari, Car_Mercedes, Car_BMW]
for car in my_cars:
    car.name = "Moskvich"
print(my_cars)  # [Car_Moskvich, Car_Moskvich, Car_Moskvich]

或者我应该迭代列表索引吗?像这样:

for car_id in range(len(my_cars)):
    my_cars[car_id].name = "Moskvich"

问题是:以上两种方式都允许还是只有第二种方式没有错误?

如果答案是肯定的,那么下面的sn-p是否有效?

lovely_numbers = [[41, 32, 17], [26, 55]]
for numbers_pair in lovely_numbers:
    numbers_pair.pop()
print(lovely_numbers)  # [[41, 32], [26]]

UPD。我想看看 python 文档,它说“这些操作是允许的”,而不是某人的假设。

【问题讨论】:

  • 是的,只要不改变长度就不会遇到问题。最后一点,如果您的列表在弹出之前为空,您将遇到 IndexError。
  • @diccza 文档也没有提到是否允许改变大小。它是。所有这些操作都是允许的,否则你会得到一个语法错误。这是一个风格问题,以及作为一名优秀的程序员你应该做什么。
  • 您已经有两个答案说修改列表本身与修改列表中的元素不同。这是正确的,但可能会让某些人感到困惑。记住列表只包含引用(即指针)可能会有所帮助。为了进一步阅读,我强烈推荐 Ned Batchelder 的 this article

标签: python list iteration


【解决方案1】:

我知道您不应该在迭代列表时添加/删除项目。但是,如果我不更改列表长度,我可以修改我正在迭代的列表中的项目吗?

您根本没有以任何方式修改列表。。您正在修改的是列表中的元素;那很好。只要不直接更改实际列表就可以了。

无需遍历索引。事实上,这是单调的。除非您实际上是在尝试更改列表本身,否则只需按值迭代列表即可。

如果答案是肯定的,那么下面的sn-p是否有效?

lovely_numbers = [[41, 32, 17], [26, 55]]
for numbers_pair in lovely_numbers:
    numbers_pair.pop()
print(lovely_numbers)  # [[41, 32], [26]]

当然。出于与我上面所说的完全相同的原因。您没有修改 lovely_numbers 本身。相反,您只是在修改 in lovely_numbers 中的元素。

【讨论】:

    【解决方案2】:

    在迭代列表元素时修改列表而不是修改列表的示例

    list_modified_during_iteration.py

    a = [1,2]
    i = 0
    for item in a:
          if i<5:
              print 'append'
              a.append(i+2)
          print a
          i += 1
    

    list_not_modified_during_iteration.py (将item改为i

    a = [1,2]
    i = 0
    for i in range(len(a)):
        if i<5:
            print 'append'
            a.append(i+2)
        print a
        i += 1
    

    【讨论】:

      【解决方案3】:

      当然可以。第一种方式是正常的,但在某些情况下你也可以使用list comprehensionsmap()

      【讨论】:

      • 您给出了答案但没有说明原因,并且在抛开术语和概念而没有描述它们对 OP 有何用处。
      【解决方案4】:

      没有修改列表,可以这么说。您只是在修改列表中的元素。我不认为这是个问题。

      要回答您的第二个问题,确实允许两种方式(如您所知,因为您运行了代码),但这取决于具体情况。内容是可变的还是不可变的?

      例如,如果您想为整数列表中的每个元素添加一个,这将不起作用:

      >>> x = [1, 2, 3, 4, 5]
      >>> for i in x:
      ...     i += 1
      ... 
      >>> x
      [1, 2, 3, 4, 5] 
      

      确实,ints 是不可变对象。相反,您需要遍历索引并更改每个索引处的元素,如下所示:

      >>> for i in range(len(x)):
      ...     x[i] += 1
      ...
      >>> x
      [2, 3, 4, 5, 6]
      

      如果您的项目是可变的,那么第一种方法(直接迭代元素而不是索引)毫无疑问更有效,因为索引的额外步骤是可以避免的开销,因为这些元素是可变的.

      【讨论】:

      • Python 确实按索引列出了迭代,所以是的,在迭代期间删除项目(从末尾除外,使用负切片)是潜在的危险行为:尽管修改它们是完全安全的。
      • 是的,听起来很合理。我只是想如果在 for 循环中我开始为一个对象创建许多附加字段(或任何其他会消耗太多内存并因此需要重新分配的操作)会发生什么,然后我完成这个对象并转到下一个一。
      • @dizzcza 在python中,每一位内存都是从堆中分配的。不能保证列表中的相邻元素在实际内存中彼此靠近。这绝对不用担心。 :)
      • 我可以在 for 循环运行时向列表中添加或删除项目吗?这是允许的吗?如果是这样,那么python将在for循环中使用新列表或原始列表
      • @variable 这不是一个好主意。我会在循环外初始化一个新列表并相应地添加项目。
      猜你喜欢
      • 2014-09-17
      • 2019-04-04
      • 2018-10-12
      • 1970-01-01
      • 2020-09-07
      • 2020-07-28
      • 1970-01-01
      相关资源
      最近更新 更多