【问题标题】:Python don't modify values while iterating over a list with zip(), but does it while with enumerate()Python 在使用 zip() 迭代列表时不会修改值,但在使用 enumerate() 时会这样做
【发布时间】:2019-06-21 01:08:36
【问题描述】:

为什么 Python 在使用索引号进行迭代时工作方式不同?例如,对于 zip 函数(在 for 循环中使用时没有索引),值不会存储在变量中。虽然使用 enumerate() 函数(允许我在 foo 循环中有一个“索引”),但它通常会发生。

当我尝试在循环内外打印时,不使用索引的解决方案对我的“宽度”变量有不同的值,这里 zip() 函数用于处理两个列表同时不需要处理它们的索引:

tableData = [['apples', 'oranges', 'cherries', 'banana'],
            ['Alice', 'Bob', 'Carol', 'David'],
            ['dogs', 'cats', 'moose', 'goose']]

def printTable(myListOfLists):
    colWidths = [0] * len(myListOfLists)
    for lists, width in zip(myListOfLists, colWidths):
        for item in lists:
            if width < len(item):
                width = len(item)
        print(width) #This prints my 'width' rightly
    print(colWidths) #This prints my 'width' wrongly

printTable(tableData)

上面的代码产生以下不需要的输出:

8
5
5
[0, 0, 0]

但是如果我使用 enumerate() (生成索引以同时处理两个列表)在我的 for 循环中修改的值将正确保存在宽度变量中,请参见代码示例:

tableData = [['apples', 'oranges', 'cherries', 'banana'],
            ['Alice', 'Bob', 'Carol', 'David'],
            ['dogs', 'cats', 'moose', 'goose']]

def printTable(myListOfLists):
    colWidths = [0] * len(myListOfLists)
    for index, lists in enumerate(myListOfLists):
        for item in lists:
            if colWidths[index] < len(item):
                colWidths[index] = len(item)
        print(colWidths[index]) #This prints my 'width' rightly
    print(colWidths) #This ALSO prints my 'width' rightly

printTable(tableData)

第二个输出是:

8
5
5
[8, 5, 5]

那么,请问如何使用 zip() 并修改我的“外部”变量?我究竟做错了什么?谢谢。

【问题讨论】:

  • 通过width = len(item),您正在使用len(item) 值重新分配变量(将其值从列表更改为另一个)width,您不是使用列表而是使用变量本身。在第二种情况下,colWidths 也是列表,但您通过更改某个索引下的项目来影响它。
  • 请问正确的做法是什么? @达米安

标签: python list for-loop variables


【解决方案1】:

我相信,您想要实现的是从tableData 获取给定子列表中最长项目的长度。为此,您可以从子列表中获取最长的字符串,然后像这样测量它的长度:

tableData = [['apples', 'oranges', 'cherries', 'banana'],
             ['Alice', 'Bob', 'Carol', 'David'],
             ['dogs', 'cats', 'moose', 'goose']]


def printTable(myListOfLists):
    colWidths = []
    for sub_list in myListOfLists:
        longest_item = max(sub_list, key=len)
        colWidths.append(len(longest_item))
    print(colWidths)


printTable(tableData)

用解释编辑:

通过使用for lists, width in zip(myListOfLists, colWidths):

在您获得的每个周期 1) 带有来自 tableData 的字符串的单个列表为lists。 2) 整数,从 colWidths 到 width 正好为 0@

然后,在每个文本(如“apples”、“oranges”等)上进行迭代。

你在这里做什么: width = len(item) 就像在说“现在宽度不再是 0,而是 len(item),这样你不会影响列表 colWidths,因为你没有使用它,你正在操纵这个 0 值,它与colWidths 列表中的位置无关,它只是在“宽度”名称下被len(item) 替换的0。

【讨论】:

  • 我尽量让你的代码保持原来的形式,这就是为什么有不同的命名约定(一种带有下划线,另一种为 CamelCase)
  • Damian,谢谢,但我正在尝试:1) 了解为什么我的代码不能按预期工作。 2) 了解如何使用 zip、for 和 if 来工作。即使这不是最好的解决方案。感谢您的解决方案,但它仍然没有回答我的问题。
  • 哦,那么对不起。我将编辑带有解释的消息。
  • 学习用的,你明白吗?我需要了解为什么会这样。那我就不会再犯同样的错误了。抱歉,如果您可以将这些“详细信息”添加到您的答案中。
  • 没问题!这更激励我解释!我的回答可能有点混乱,但我希望你能明白这一点。
【解决方案2】:

enumerate 是一种 Pythonic 生成索引的方式(类似于,但更惯用的 for i in range(len(...):

colWidths = [0] * len(myListOfLists)
for index, lists in enumerate(myListOfLists):
    for item in lists:
        if colWidths[index] < len(item):
            colWidths[index] = len(item)

这里的listsitem 来自myListOfLists,但您明确修改了colWidths 列表。

在第一种情况下,您正在修改迭代变量,而不是源列表:

colWidths = [0] * len(myListOfLists)
for lists, width in zip(myListOfLists, colWidths):
    for item in lists:
        if width < len(item):
            width = len(item)

width 是一个迭代变量。但是width = len(item) 行为变量(名称)分配了一个新值,从而中断了它与源数组的链接。在下一次迭代中,width 再次由迭代器设置。源列表colWidths 没有改变。

这种模式适用于简单的情况:

alist = [1,2,3,4]
for i in alist:
   i = 5     # does nothing to alist

for i in range(4):
    alist[i] = 5    # changes an element of alist

对迭代变量的更改很棘手。您必须了解它何时代表“可变”的东西,而不是。并了解赋值给变量和修改对象(如列表)之间的区别。

【讨论】:

    【解决方案3】:

    通过这种方式,您不会更改列表的值,您需要索引来更改实际值。

    示例:

    for e in [1, 2, 3]:
        e = 10  # this way is wrong 
    
    data = [1, 2, 3]
    for index, e in enumerate(data):
        e[index] = 50 # always it is needed an index  
    

    解决问题的好方法:

    tableData = [['apples', 'oranges', 'cherries', 'banana'],
                 ['Alice', 'Bob', 'Carol', 'David'],
                 ['dogs', 'cats', 'moose', 'goose']]
    
    
    data = [len(max(e, key=len)) for e in tableData]
    
    print(data)
    

    tableData = [['apples', 'oranges', 'cherries', 'banana'],
                ['Alice', 'Bob', 'Carol', 'David'],
                ['dogs', 'cats', 'moose', 'goose']]
    
    def printTable(myListOfLists):
        colWidths = [0] * len(myListOfLists)
        for index, row in enumerate(myListOfLists):
            colWidths[index] = len(max(row, key=len))
        print(colWidths)
    
    printTable(tableData)
    

    另一方面,您可以尝试一下。

    for i, (a, b) in enumerate(zip(alist, blist)):
        print i, a, b
    

    【讨论】:

    • 谢谢,我想你已经完全回答了。特别是因为如此客观和“for i, (a, b) in enumerate(zip(alist, blist)): print i, a, b"
    猜你喜欢
    • 2018-10-12
    • 2020-09-11
    • 2023-03-06
    • 2010-10-19
    • 2020-09-07
    • 2014-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多