例如,如果我正在制作一个简单的基于网格的游戏,我可能会有一些2d列表.一个可能是地形,另一个可能是对象等.不幸的是,当我需要迭代列表并让一个列表中的正方形内容影响另一个列表的一部分时,我必须做这样的事情.
for i in range(len(alist)): for j in range(len(alist[i])): if alist[i][j].isWhatever: blist[i][j].doSomething()
有没有更好的方法来做这样的事情?
如果有人对上述解决方案的性能感兴趣,那么它们适用于4000x4000网格,从最快到最慢:
布莱恩:1.08s(修改后,izip
代替zip
)
约翰:2.33秒
DzinX:2.36s
ΤΖΩΤΖΙΟΥ:2.41s(但对象初始化需要62s)
尤金:3.17s
罗伯特:4.56s
布莱恩:27.24s(原创,附zip
)
编辑:通过izip
修改添加了Brian的分数,它获得了大量的胜利!
约翰的解决方案也很快,虽然它使用指数(我真的很惊讶看到这个!),而罗伯特和布莱恩的(与 zip
)比问题创建者的初始解决方案慢.
所以让我们介绍Brian的获胜函数,因为它在这个帖子的任何地方都没有以适当的形式显示:
from itertools import izip for a_row,b_row in izip(alist, blist): for a_item, b_item in izip(a_row,b_row): if a_item.isWhatever: b_item.doSomething()
我首先编写一个生成器方法:
def grid_objects(alist, blist): for i in range(len(alist)): for j in range(len(alist[i])): yield(alist[i][j], blist[i][j])
然后,无论何时需要迭代列表,您的代码都如下所示:
for (a, b) in grid_objects(alist, blist): if a.is_whatever(): b.do_something()
你可以压缩它们.即:
for a_row,b_row in zip(alist, blist): for a_item, b_item in zip(a_row,b_row): if a_item.isWhatever: b_item.doSomething()
但是,如果您很少实际使用b_item(即a_item.isWhatever通常为False),则对项目进行压缩和迭代的开销可能会高于原始方法.您可以使用itertools.izip而不是zip来减少内存对此的影响,但除非您总是需要b_item,否则它可能会稍微慢一些.
或者,考虑使用3D列表,因此单元格i,j的地形位于l [i] [j] [0],l [i] [j] [1]等处的对象,甚至可以将对象组合在一起可以做[i] [j] .terrain,a [i] [j] .object等
[编辑] DzinX的时间实际上表明额外检查b_item的影响并不是很重要,接下来是通过索引重新查找的性能损失,所以上面(使用izip)似乎是最快的.
我现在也对3d方法进行了快速测试,但它似乎仍然更快,所以如果你能以这种形式存储数据,那么访问它可能更简单,更快捷.以下是使用它的示例:
# Initialise 3d list: alist = [ [[A(a_args), B(b_args)] for i in xrange(WIDTH)] for j in xrange(HEIGHT)] # Process it: for row in xlist: for a,b in row: if a.isWhatever(): b.doSomething()
以下是我使用1000x1000阵列进行10次循环的时间,各种比例的isWhatever是真的:
( Chance isWhatever is True ) Method 100% 50% 10% 1% 3d 3.422 2.151 1.067 0.824 izip 3.647 2.383 1.282 0.985 original 5.422 3.426 1.891 1.534