我怎么能在python中一次遍历多个2d列表呢?

Eug*_*e M 22 python

例如,如果我正在制作一个简单的基于网格的游戏,我可能会有一些2d列表.一个可能是地形,另一个可能是对象等.不幸的是,当我需要迭代列表并让一个列表中的正方形内容影响另一个列表的一部分时,我必须做这样的事情.

for i in range(len(alist)):
    for j in range(len(alist[i])):
        if alist[i][j].isWhatever:
            blist[i][j].doSomething()
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来做这样的事情?

Dzi*_*inX 32

如果有人对上述解决方案的性能感兴趣,那么它们适用于4000x4000网格,从最快到最慢:

编辑:通过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()
Run Code Online (Sandbox Code Playgroud)


Rob*_*ney 15

我首先编写一个生成器方法:

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])
Run Code Online (Sandbox Code Playgroud)

然后,无论何时需要迭代列表,您的代码都如下所示:

for (a, b) in grid_objects(alist, blist):
    if a.is_whatever():
        b.do_something()
Run Code Online (Sandbox Code Playgroud)


Bri*_*ian 10

你可以压缩它们.即:

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()
Run Code Online (Sandbox Code Playgroud)

但是,如果您很少实际使用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()
Run Code Online (Sandbox Code Playgroud)

以下是我使用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
Run Code Online (Sandbox Code Playgroud)


Ant*_*sma 5

当您使用数字网格并希望获得真正良好的性能时,您应该考虑使用Numpy。它非常容易使用,让您可以思考网格的操作而不是网格上的循环。性能来自于这样一个事实:操作随后使用优化的 SSE 代码在整个网格上运行。

例如,这里是我编写的一些使用 numpy 的代码,该代码对由弹簧连接的带电粒子进行强力数值模拟。此代码在 31 毫秒内计算具有 100 个节点和 99 个边的 3d 系统的时间步长。这比我能想到的最好的纯 Python 代码快 10 倍以上。

from numpy import array, sqrt, float32, newaxis
def evolve(points, velocities, edges, timestep=0.01, charge=0.1, mass=1., edgelen=0.5, dampen=0.95):
    """Evolve a n body system of electrostatically repulsive nodes connected by
       springs by one timestep."""
    velocities *= dampen

    # calculate matrix of distance vectors between all points and their lengths squared
    dists = array([[p2 - p1 for p2 in points] for p1 in points])
    l_2 = (dists*dists).sum(axis=2)
    
    # make the diagonal 1's to avoid division by zero
    for i in xrange(points.shape[0]):
        l_2[i,i] = 1

    l_2_inv = 1/l_2
    l_3_inv = l_2_inv*sqrt(l_2_inv)

    # repulsive force: distance vectors divided by length cubed, summed and multiplied by scale
    scale = timestep*charge*charge/mass
    velocities -= scale*(l_3_inv[:,:,newaxis].repeat(points.shape[1], axis=2)*dists).sum(axis=1)

    # calculate spring contributions for each point
    for idx, (point, outedges) in enumerate(izip(points, edges)):
        edgevecs = point - points.take(outedges, axis=0)
        edgevec_lens = sqrt((edgevecs*edgevecs).sum(axis=1))
        scale = timestep/mass
        velocities[idx] += (edgevecs*((((edgelen*scale)/edgevec_lens - scale))[:,newaxis].repeat(points.shape[1],axis=1))).sum(axis=0)

    # move points to new positions
    points += velocities*timestep
Run Code Online (Sandbox Code Playgroud)