例如,如果我正在制作一个简单的基于网格的游戏,我可能会有一些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
代替zip
)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()
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)
当您使用数字网格并希望获得真正良好的性能时,您应该考虑使用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)