【问题标题】:Python: fastest way to create a list of n listsPython:创建 n 个列表的最快方法
【发布时间】:2023-03-07 14:33:01
【问题描述】:

所以我想知道如何最好地创建一个空白列表:

[[],[],[]...]

由于 Python 如何处理内存中的列表,这不起作用:

[[]]*n

这确实创建了[[],[],...],但每个元素都是同一个列表:

d = [[]]*n
d[0].append(1)
#[[1],[1],...]

类似于列表推导的工作:

d = [[] for x in xrange(0,n)]

但这使用 Python VM 进行循环。有没有办法使用隐含循环(利用它是用 C 编写的)?

d = []
map(lambda n: d.append([]),xrange(0,10))

这实际上更慢。 :(

【问题讨论】:

  • 如果有什么比d = [[] for x in xrange(0,n)] 快得多,我会感到惊讶。您要么必须在 Python 中显式循环,要么重复调用 Python 函数/lambda(这应该更慢)。但仍然希望有人会发布一些表明我错了的东西:)。
  • 当你用timeit测量这些时,你学到了什么?
  • 我刚刚确认map(lambda x: [], xrange(n)) 比列表理解要慢。

标签: python


【解决方案1】:

可能是唯一比

稍微快一点的方法
d = [[] for x in xrange(n)]

from itertools import repeat
d = [[] for i in repeat(None, n)]

它不必在每次迭代中创建一个新的int 对象,并且在我的机器上快了大约 15%。

编辑:使用 NumPy,可以避免 Python 循环使用

d = numpy.empty((n, 0)).tolist()

但这实际上比列表理解慢 2.5 倍。

【讨论】:

  • map(lambda x:[], repeat(None,n)) 怎么样?
  • @Paul:由于 lambda 表达式的函数调用开销,这会再次变慢。
  • 既然 Python 3 中的范围不同,不应该更新吗?
  • @beruic 我只是在第一个代码块中引用问题中的代码,所以改变它并没有意义。
【解决方案2】:

列表解析实际上比显式循环更有效地实现(请参阅the dis output for example functions),map 方式必须在每次迭代时调用一个不透明的可调用对象,这会产生相当大的开销。

无论如何,[[] for _dummy in xrange(n)] 是正确的做法,并且其他各种方式之间的微小(如果存在的话)速度差异应该 无关紧要。当然,除非你把大部分时间都花在这上面——但在这种情况下,你应该改用你的算法。您多久创建一次这些列表?

【讨论】:

  • 请不要将_ 作为变量名!否则很好的答案:)
  • @Sven:为什么不呢?它通常用于未使用的变量(如果它被称为i,我会寻找它的使用位置)。唯一的缺陷是它掩盖了在 REPL 中保存最后一个结果的 _ ......而这只是在 2.x 中列表解析泄漏的情况。
  • 不经常,这就是我继续使用列表理解的原因。认为看看人们不得不说的话会很有趣。我在 PyCon 上看到了 Dropbox 的演讲,使用 itertools.imap 而不是 for 循环来更新 md5 散列的次数非常多,从那时起我就对 C 循环有点着迷了。
  • 不使用它的最重要原因是它容易混淆人们,使他们认为这是某种特殊的语法。并且除了与交互式解释器中的_冲突外,还与常见的gettext别名冲突。如果您想明确该变量是虚拟变量,请称其为dummy,而不是_
【解决方案3】:

这里有两种方法,一种甜美而简单(并且是概念性的),另一种更正式,可以在读取数据集后在各种情况下进行扩展。

方法一:概念性

X2=[]
X1=[1,2,3]
X2.append(X1)
X3=[4,5,6]
X2.append(X3)
X2 thus has [[1,2,3],[4,5,6]] ie a list of lists. 

方法二:形式化和可扩展

另一种将列表存储为不同数字列表的优雅方式 - 它从文件中读取。 (这里的文件有数据集火车) Train 是一个具有 50 行和 20 列的数据集。 IE。 Train[0] 给我 csv 文件的第一行,train[1] 给我第二行,依此类推。我有兴趣将 50 行的数据集作为一个列表分开,除了列 0 ,这是我在这里解释的变量,因此必须从原始火车数据集中删除,然后在列表之后扩大列表 - 即列表的列表.这是执行此操作的代码。

请注意,我是从内部循环中的“1”读取的,因为我只对解释变量感兴趣。我在另一个循环中重新初始化 X1=[] ,否则 X2.append([0:(len(train[0])-1)]) 将一遍又一遍地重写 X1 - 除了内存效率更高。

X2=[]
for j in range(0,len(train)):
    X1=[]
    for k in range(1,len(train[0])):
        txt2=train[j][k]
        X1.append(txt2)
    X2.append(X1[0:(len(train[0])-1)])

【讨论】:

    【解决方案4】:

    所以我做了一些速度比较以获得最快的方式。 列表推导确实非常快。接近的唯一方法是避免在构建列表期间执行字节码。 我的第一次尝试是以下方法,原则上似乎更快:

    l = [[]]
    for _ in range(n): l.extend(map(list,l))
    

    (当然会产生一个长度为 2**n 的列表) 根据 timeit,对于短列表和长(一百万)列表,这种构造速度是列表理解的两倍。

    我的第二次尝试是使用 starmap 为我调用列表构造函数,有一个构造函数似乎以最快的速度运行列表构造函数,但仍然较慢,但只有很小的量:

    from itertools import starmap
    l = list(starmap(list,[()]*(1<<n)))
    

    有趣的是,执行时间表明最终的列表调用使星图解决方案变慢,因为它的执行时间几乎完全等于以下速度:

    l = list([] for _ in range(1<<n))
    

    当我意识到 list(()) 也会产生一个列表时,我的第三次尝试来了,所以我尝试了看似简单的方法:

    l = list(map(list, [()]*(1<<n)))
    

    但这比调用星图要慢。

    结论:对于速度狂人: 一定要使用列表理解。 如果必须,仅调用函数。 使用内置函数。

    【讨论】:

      【解决方案5】:

      要创建列表和列表列表,请使用以下语法

           x = [[] for i in range(10)]
      

      这将创建一维列表并将其初始化,将数字放入 [[number] 并设置列表长度,将长度放入范围(长度)

      • 要创建列表列表,请使用以下语法。
          x = [[[0] for i in range(3)] for i in range(10)]
      

      这将初始化 10*3 维度和值为 0 的列表列表

      • 访问/操作元素
          x[1][5]=value
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-09-20
        • 1970-01-01
        • 2015-03-07
        • 2010-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多