更优雅地初始化Python中的重复项列表的方法
如果我想要一个初始化为5个零的列表,那是非常简单的:
[0] * 5
但是如果我把代码改成更复杂的数据结构,比如一个零的列表:
[[0]] * 5
这样就不能按预期工作,因为它会生成10个相同列表的副本。我必须这样做:
[[0] for i in xrange(5)]
这样感觉有点繁琐,而且还需要用到一个变量,所以有时候我甚至会这样做:
[[0] for _ in " "]
但是如果我想要一个包含零的列表的列表,那就更麻烦了:
[[[0] for _ in " "] for _ in " "]
这一切都不是我想要的:
[[[0]]*5]*5
有没有人找到一个优雅的方法来解决这个“问题”?
4 个回答
2
另一个方法是扩展列表类:
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
然后:
>>> hey = mlist([mlist([0])])
>>> hey
[[0]]
>>> hey * 4
[[0], [0], [0], [0]]
>>> blah = hey * 4
>>> blah[0][0] = 9
>>> blah
[[9], [0], [0], [0]]
不过初始化这个 mlist
还是挺麻烦的。
5
如果我经常需要创建多层嵌套的列表,我会把这个过程封装成一个小的工厂函数,比如:
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)
实际上,我甚至懒得这么做,因为我用到这种多维列表的情况很少,所以直接用简单的列表推导就可以了。不过,自己建立一个小工具函数模块的想法是很不错的,特别是对于那些你需要频繁做的简单任务,而这些任务用简单的代码写起来又不够优雅时,这样做真的是最好的选择!-)
9
经过一番思考,我想出了这个解决方案:(没有导入的情况下,只有7行代码)
# 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]]]
还有另一种解决方案,它的好处是可以使用“[[[0]]*5]*5”这种语法:
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]]]
顺便提一下,numpy库有一个np.zeros(s)
的函数,其中s
是一个形状,比如(3,4,5)
>>> 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
我发现这个问题真的很有趣。所以,谢谢你的提问 :)