How can I, in python, iterate over multiple 2d lists at once, cleanly?

后端 未结 10 1049
予麋鹿
予麋鹿 2021-02-05 05:15

If I\'m making a simple grid based game, for example, I might have a few 2d lists. One might be for terrain, another might be for objects, etc. Unfortunately, when I need to ite

相关标签:
10条回答
  • 2021-02-05 06:03

    If a.isWhatever is rarely true you could build an "index" once:

    a_index = set((i,j) 
                  for i,arow in enumerate(a) 
                  for j,a in enumerate(arow) 
                  if a.IsWhatever())
    

    and each time you want something to be done:

    for (i,j) in a_index:
        b[i][j].doSomething()
    

    If a changes over time, then you will need to keep the index up-to-date. That's why I used a set, so items can be added and removed fast.

    0 讨论(0)
  • 2021-02-05 06:05

    If the two 2D-lists remain constant during the lifetime of your game and you can't enjoy Python's multiple inheritance to join the alist[i][j] and blist[i][j] object classes (as others have suggested), you could add a pointer to the corresponding b item in each a item after the lists are created, like this:

    for a_row, b_row  in itertools.izip(alist, blist):
        for a_item, b_item in itertools.izip(a_row, b_row):
            a_item.b_item= b_item
    

    Various optimisations can apply here, like your classes having __slots__ defined, or the initialization code above could be merged with your own initialization code e.t.c. After that, your loop will become:

    for a_row in alist:
        for a_item in a_row:
            if a_item.isWhatever():
                a_item.b_item.doSomething()
    

    That should be more efficient.

    0 讨论(0)
  • 2021-02-05 06:06

    You could zip them. ie:

    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()
    

    However the overhead of zipping and iterating over the items may be higher than your original method if you rarely actually use the b_item (ie a_item.isWhatever is usually False). You could use itertools.izip instead of zip to reduce the memory impact of this, but its still probably going to be slightly slower unless you always need the b_item.

    Alternatively, consider using a 3D list instead, so terrain for cell i,j is at l[i][j][0], objects at l[i][j][1] etc, or even combine the objects so you can do a[i][j].terrain, a[i][j].object etc.

    [Edit] DzinX's timings actually show that the impact of the extra check for b_item isn't really significant, next to the performance penalty of re-looking up by index, so the above (using izip) seems to be fastest.

    I've now given a quick test for the 3d approach as well, and it seems faster still, so if you can store your data in that form, it could be both simpler and faster to access. Here's an example of using it:

    # 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()
    

    Here are my timings for 10 loops using a 1000x1000 array, with various proportions of isWhatever being true are:

                ( 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
    
    0 讨论(0)
  • 2021-02-05 06:21
    for d1 in alist
       for d2 in d1
          if d2 = "whatever"
              do_my_thing()
    
    0 讨论(0)
提交回复
热议问题