在Python中初始化重复项列表的更优雅的方法

在Python中初始化重复项列表的更优雅的方法,python,Python,如果我想要一个初始化为5个零的列表,这非常简单: [0] * 5 但是,如果我将代码更改为一个更复杂的数据结构,如零列表: [[0]] * 5 将无法按预期工作,因为它将是同一列表的10份副本。我必须做: [[0] for i in xrange(5)] 这感觉很笨重,并且使用了一个变量,所以有时我甚至会: [[0] for _ in " "] 但是如果我想要一个由零组成的列表,它会变得更丑陋: [[[0] for _ in " "] for _ in " "]

如果我想要一个初始化为5个零的列表,这非常简单:

[0] * 5
但是,如果我将代码更改为一个更复杂的数据结构,如零列表:

[[0]] * 5
将无法按预期工作,因为它将是同一列表的10份副本。我必须做:

[[0] for i in xrange(5)]
这感觉很笨重,并且使用了一个变量,所以有时我甚至会:

[[0] for _ in "     "]
但是如果我想要一个由零组成的列表,它会变得更丑陋:

[[[0] for _ in "     "] for _ in "     "]
所有这些,而不是我想做的:

[[[0]]*5]*5

有没有人找到一种优雅的方法来处理这个“问题”?

一种解决方案是使用助手功能:

import copy
def r(i,n):
    return [copy.deepcopy(i) for _ in xrange(n)]
然后:


但是这种语法很难看。

另一种是扩展list类:

import copy
class mlist(list):
  def __mul__(self, n):
    res = mlist()
    for _ in xrange(n):
      for l in self:
    res.append(copy.deepcopy(l))
  return res
然后:


但是初始化
mlist
很烦人。

如果我经常需要一个列表,一个列表,一个列表,一个列表。。。我只需将其建筑打包成一个小型工厂功能,例如:

import copy

def multi_dimension_list(baseitem, *dimensions):
  dimensions = list(dimensions)
  result = [baseitem] * dimensions.pop(-1)
  for d in reversed(dimensions):
    result = [copy.deepcopy(result) for _ in range(d)]
  return result

eg = multi_dimension_list(0, 3, 4, 5)
print(eg)
# and just to prove the parts are independent...:
eg[1][1][1] = 23
print(eg)

在实践中,我甚至不费心,因为我对这种多维列表的使用很少,所以内联列表的理解也很好。然而,为您确实需要经常执行且(在您看来)不能通过内联习惯用法优雅地完成的简单任务构建您自己的小实用程序功能模块的总体思路,实际上是唯一的方法!)

经过思考,我想出了这个解决方案:(7行,不带导入)

试验得出以下结果:

>>> from pprint import pprint as pp
>>> 
>>> matrix(None, 2,3)
[[None, None, None], [None, None, None]]
>>> 
>>> matrix(None, 4,3)
[[None, None, None], [None, None, None], [None, None, None], [None, None, None]]
>>> 
>>> x = matrix(None, 3,5,2)
>>> pp(x)
[[[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]]]
>>> x[1][3][0] = "test"
>>> pp(x)
[[[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], ['test', None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]]]

另一个解决方案的优点是使用“[[0]]*5]*5”语法:

测试:


顺便说一句,numpy库有一个
np.zeros(s)
-函数,其中
s
是一个类似
(3,4,5)


最后是性能测试:

# functions are already defined ...
import timeit
>>> # Alex Martelli's Code
>>> t1 = timeit.Timer( lambda: multi_dimension_list(None, 3,4,5) )
>>> # the two mentioned above
>>> t2 = timeit.Timer( lambda: matrix(None, 3,4,5) )
>>> t3 = timeit.Timer( lambda: uniq(None, [[[None]*5]*4]*3) )
>>> 
>>> t1.timeit(10000)
2.1910018920898438
>>> t2.timeit(10000)
0.44953203201293945
>>> t3.timeit(10000)
0.48807907104492188

我发现发现这个问题真的很有趣。所以,谢谢你的提问:)

我运行这个函数时遇到了一个问题。eg[1][1][1]赋值操作应仅更改元素位置eg[1][1]。但我发现它改变了三个元素的值。它是有线的…我唯一需要多维列表的时候就是我想要矩阵的时候。所以我只是用numpy来代替。@THC4k,是的,这无疑是我发现自己很少使用多维列表的一个主要原因。克劳迪乌,我添加了一个新方法和一些细节,忘了!这个问题有最好的答案,那就是使用numpy
# helper
def cl(n, func):
    # return a lambda, that returns a list, where func(tion) is called
    return (lambda: [func() for _ in range(n)])

def matrix(base, *ns):
    # the grid lambda (at the start it returns the base-element)
    grid = lambda: base

    # traverse reversed, to handle the midmost values first
    for n in reversed(ns):
        # assign a new lambda with the last grid within (and call it)
        grid = cl(n, grid)

    return grid() # call the full grid (but the matrix calls you ^^)
>>> from pprint import pprint as pp
>>> 
>>> matrix(None, 2,3)
[[None, None, None], [None, None, None]]
>>> 
>>> matrix(None, 4,3)
[[None, None, None], [None, None, None], [None, None, None], [None, None, None]]
>>> 
>>> x = matrix(None, 3,5,2)
>>> pp(x)
[[[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]]]
>>> x[1][3][0] = "test"
>>> pp(x)
[[[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], ['test', None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]]]
def uniq(base, l):
    # function used to replace all values with the base
    nl = []
    for i in l:
        if type(i) is list:
            nl.append(uniq(base, i)) # recursion for deep lists
        else:
            nl.append(base)
    return nl
# first arg is the base, the 0 inside the [] is just a dummy
# (for what None is the best choice usually)
>>> x = uniq(0, [[[0]]*5]*5)
>>> x[0][3][0] = 5
>>> pp(x)
[[[0], [0], [0], [5], [0]],
 [[0], [0], [0], [0], [0]],
 [[0], [0], [0], [0], [0]],
 [[0], [0], [0], [0], [0]],
 [[0], [0], [0], [0], [0]]]
>>> s = (2,2)
>>> np.zeros(s)
array([[ 0.,  0.],
       [ 0.,  0.]])
# functions are already defined ...
import timeit
>>> # Alex Martelli's Code
>>> t1 = timeit.Timer( lambda: multi_dimension_list(None, 3,4,5) )
>>> # the two mentioned above
>>> t2 = timeit.Timer( lambda: matrix(None, 3,4,5) )
>>> t3 = timeit.Timer( lambda: uniq(None, [[[None]*5]*4]*3) )
>>> 
>>> t1.timeit(10000)
2.1910018920898438
>>> t2.timeit(10000)
0.44953203201293945
>>> t3.timeit(10000)
0.48807907104492188