【问题标题】:Most elegant way to modify elements of nested lists in place修改嵌套列表元素的最优雅方式
【发布时间】:2011-08-06 01:17:39
【问题描述】:

我有一个如下所示的 2D 列表:

table = [['donkey', '2', '1', '0'], ['goat', '5', '3', '2']]

想把最后三个元素改成整数,但是下面的代码感觉很丑:

for row in table:
    for i in range(len(row)-1):
        row[i+1] = int(row[i+1])

但我更喜欢看起来像这样的东西:

for row in table:
    for col in row[1:]:
        col = int(col)

我认为应该有一种方法可以编写上面的代码,但是切片会创建一个与原始列表分开的迭代器/新列表,因此引用不会继续存在。

有没有办法获得更 Pythonic 的解决方案?

【问题讨论】:

    标签: python nested-lists


    【解决方案1】:
    for row in table:
        row[1:] = [int(c) for c in row[1:]]
    

    上面看起来更像pythonic吗?

    【讨论】:

    • 虽然这在技术上是一个就地操作,但在循环内创建了 两个 额外列表。第一个是由row[1:] 切片(map 的参数)创建的。第二个是通过使用列表推导式创建的。
    • 这被称为“列表推导”[docs.python.org/2/tutorial/…
    【解决方案2】:

    试试:

    >>> for row in table:
    ...     row[1:]=map(int,row[1:])
    ... 
    >>> table
    [['donkey', 2, 1, 0], ['goat', 5, 3, 2]]
    

    AFAIK,分配给list 切片会强制操作就地完成,而不是创建新的list

    【讨论】:

    • 在理解上使用 map 仍然被认为是 Pythonic 吗?
    • @Nicholas Mancuso:我认为两者在性能方面是相似的。我认为map 的可读性并不低(尤其是如果您熟悉函数式编程)。所以,我想这取决于个人喜好。我同时使用两者,在特定情况下看起来更简单的是最好的恕我直言。尽管 AFAIK,Guido 认为列表理解更好。但我没有看到任何客观原因。
    • @Nicholas Mancuso map 完全是pythonic。与 Python 不同的是,当您尝试过多地塞入 lambda 以使用它时,您经常会遇到纠结的混乱。
    • @MAK:为什么在您最后的评论和答案的第二行中有额外的:?它不是必需的,而且是噪音。
    • 虽然这在技术上是一个就地操作,但在循环内创建了 两个 额外列表。第一个是由row[1:] 切片(map 的参数)创建的。第二个是由map 创建的。对于长度为 nrow,空间使用量为 3n-1。随着 n 的增加,使用简单的内部循环变得更加节省空间。
    【解决方案3】:

    我非常喜欢 Shekhar 的回答。

    作为一般规则,在编写 Python 代码时,如果你发现自己在写 for i in range(len(somelist)),那你就错了:

    • 如果您只有一个列表,请尝试 enumerate
    • 如果您有 2 个或更多要并行迭代的列表,请尝试 zipitertools.izip

    在你的情况下,第一列是不同的,所以你不能优雅地使用枚举:

    for row in table:
        for i, val in enumerate(row):
            if i == 0: continue
            row[i] = int(val)
    

    【讨论】:

    • “如果你发现自己在写for i in range(len(somelist)),那你就做错了”——这可能是有人可以给学习pythonic习语的人最好的建议。 Python 的优势在于它为您提供的可读性,当有人从像 Java 这样的语言转换时,这种结构是典型的,他们真的会错过使用 Python 工作的真正优势。 +1
    • 您可以使用for i, val in enumerate(row[1:]): 对其进行一些改进,从而摆脱if i == 0
    • @erickrf 这会创建行的浅表副本,之后您需要使用 row[i+1] = int(val)。不确定这是否会改善很多。
    • “如果你发现自己在写for i in range(len(somelist)),那么你做错了什么”的正确性值得怀疑。 Enumerate 不仅速度较慢,而且可能被认为可读性较差。参考this answer
    【解决方案4】:

    只需使用两个参数调用 range 即可改进您的“丑陋”代码:

    for row in table:
        for i in range(1, len(row)):
            row[i] = int(row[i])
    

    如果您坚持更改项目而不分配新的临时列表(通过使用列表理解、map 和/或切片),这可能是您能做的最好的事情。见Is there an in-place equivalent to 'map' in python?

    虽然我不推荐,但是你也可以通过引入自己的in-place map函数让这段代码更通用:

    def inplacemap(f, items, start=0, end=None):
        """Applies ``f`` to each item in the iterable ``items`` between the range
        ``start`` and ``end``."""
        # If end was not specified, make it the length of the iterable
        # We avoid setting end in the parameter list to force it to be evaluated on
        # each invocation
        if end is None:
            end = len(items)
        for i in range(start, end):
            items[i] = f(items[i])
    
    for row in table:
        inplacemap(int, row, 1)
    

    就我个人而言,我觉得这个 less Pythonic。最好只有一种明显的方法可以做到这一点,但事实并非如此。

    【讨论】:

      【解决方案5】:

      使用列表推导:

      table = [row[0] + [int(col) for col in row[1:]] for row in table]
      

      【讨论】:

      • +1 我不知道你可以像这样将列表推导式链接在一起!我可能不会使用这种嵌套的东西,因为我和很多人一起工作,他们可能会觉得这不可读,但我肯定会在个人项目中记住这一点。谢谢!
      【解决方案6】:

      这将起作用:

      table = [[row[0]] + [int(v) for v in row[1:]] for row in table]
      

      但是,您可能需要考虑在首次创建列表时进行转换。

      【讨论】:

      • 你是对的。在我的所有其他算法中,用其中的特殊子表处理我的表非常麻烦,所以我现在有了一个原始数据表。
      【解决方案7】:

      这实现了您正在寻找的东西。这是一个可读的解决方案。您也可以使用 listcomp 进行类似的操作。

      >>> for row in table:
      ...     for i, elem in enumerate(row):
      ...             try:
      ...                     int(elem)
      ...             except ValueError:
      ...                     pass
      ...             else:
      ...                     row[i] = int(elem)
      ... 
      

      【讨论】:

      • 正确验证的唯一答案。虽然执行两次 int 转换很浪费,并且破坏了 try-except 块背后的想法
      猜你喜欢
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-23
      • 2017-06-06
      • 1970-01-01
      相关资源
      最近更新 更多