【问题标题】:Flattening a Python list without creating copies of any objects?展平 Python 列表而不创建任何对象的副本?
【发布时间】:2012-08-21 09:57:45
【问题描述】:

所以我正在用 Python 编写一个游戏,而我试图解决的一个问题需要我将一个 2D 列表(即列表的列表)转换为 1D 列表。我已经看到了几种方法来做到这一点,但我不知道它们中的任何一个是创建包含在其中的任何对象的副本还是只是新的引用。老实说,Python 标准库让我感到困惑,因为对象/序列是被复制还是只是被引用填充并不总是很明显。

这些是关于我的特殊情况的假设;

  • 我们可以假设我的二维列表(如果表示为矩阵)是完美的矩形。
  • 如果我能找到一个解决方案,让上述假设不成立,但目前不需要。
  • 无需担心 3D+ 列表。

谢谢!

【问题讨论】:

    标签: python arrays list multidimensional-array flatten


    【解决方案1】:

    虽然我不能代表您可能看到的所有方式,但由于 Python 的对象语义,一般不会制作副本。

    >>> a = [[1,2.3,'3', object()], [None, [2,3], 4j]]
    >>> b = [v for row in a for v in row]
    >>> a
    [[1, 2.3, '3', <object object at 0x1002af090>], [None, [2, 3], 4j]]
    >>> b
    [1, 2.3, '3', <object object at 0x1002af090>, None, [2, 3], 4j]
    >>> [id(obj) for row in a for obj in row]
    [4298185464, 4298195480, 4299558664, 4297781392, 4296523616, 4299692656, 4297773296]
    >>> [id(obj) for obj in b]
    [4298185464, 4298195480, 4299558664, 4297781392, 4296523616, 4299692656, 4297773296]
    

    v 并不表示“某个新对象等于v”,它仅表示v,即对象本身。

    【讨论】:

    • 我不得不暂时绕开那里的语法,因为我最初预计它会类似于[v for v in row for row in a]。一旦我意识到逻辑的嵌套性质(这只是嵌套的 for 循环),语法就开始单击了。我自己不会想出来的。谢谢!
    【解决方案2】:

    关于您关于副本/引用的问题,Python 以不同的方式处理可变/不可变变量:对于不可变变量(int、float、string、tuples 等),您将获得副本,对于可变(其他所有)-“引用”。

    关于python列表的扁平化:查看itertools模块。你可以这样做:

    >>> from itertools import chain
    >>> chain(*[[1,2,3], [4,5,6], [7,8,9]])
    <itertools.chain object at 0x104ff5110>
    >>> list(_)
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    【讨论】:

    • 第 2 行的星号是怎么回事?
    • 无论二维列表中的对象如何,都不会创建副本。您可以使用is 操作员检查身份。
    • @JesseTG:这是参数解包(参见here)。基本上,f(*(2,3)) == f(2,3).
    • 还有chain.from_iterable([[1,2,3], [4,5,6], [7,8,9]])
    【解决方案3】:

    可能python documentation 的这篇文章可以帮助您了解python 的作用:

    没有人“拥有”一个对象;但是,您可以拥有对 目的。对象的引用计数现在定义为 拥有对它的引用。引用的所有者负责 当不再需要引用时调用 Py_DECREF()。所有权 可以转移参考。有三种方法可以处理 拥有的引用:传递、存储或调用 Py_DECREF()。 忘记释放拥有的引用会导致内存泄漏。

    现在,这指的是在 C 代码中使用 python 对象,但是,由于解释器正是这样做的,因此您可以了解在 python 中如何管理对象和引用。

    “没有人拥有一个对象”,所以当你把一个项目放到一个列表中时,这个列表复制这个项目,它只是拥有一个引用。实际上,它会复制一个指针并增加引用计数(并且可能还会减少被替换的其他对象)。

    从python的角度来看,你不能访问对象,只能引用对象。 您可以放入变量中的任何内容实际上都是对对象的引用。 因此[1,2].append(3) 不会复制 3,而只是引用。

    如果你想复制一个对象,那么你应该使用copy 模块 或者,对于某些类型,使用对象作为参数调用类(例如 int(1234) 创建“1234”的副本,即使 python 将 -5 到 256 的整数和小字符串存储在一个小数组中,所以这样做 @987654325 @ 不复制“42”)

    【讨论】:

    • 描述是 CPython 特定的。但是对于所有 Python 实现(Pypy、Jython、IronPython),OP 的答案都是相同的:不会创建副本。
    • 好吧,他没有提到使用某些特定的python实现,所以我假设CPython。
    【解决方案4】:

    你可以这样做:

    arr2d = [[1,2,3],[4,5,6],[7,8,9]]
    arr1d = []
    for x in arr2d:
       arr1d.extend(x)
    

    这不会创建副本,更改一维数组,它不会反映。为了以后的referecne,如果你想确保它不被复制,导入copy模块,并使用copy.deepcopy(array)而不是array,你会很安全。

    【讨论】:

      猜你喜欢
      • 2023-02-08
      • 2017-05-22
      • 2011-06-15
      • 2016-08-08
      相关资源
      最近更新 更多