Ben Hughes
Ben Hughes

Reputation: 2547

What's a neater, more pythonic way to do the following enumeration?

for row, instrument in enumerate(instruments):
    for col, value in enumerate(instrument):
         self.table.SetValue(row, col, value)

Upvotes: 1

Views: 348

Answers (4)

codeape
codeape

Reputation: 100766

One option is using a list comprehension:

[self.table.setValue(row, col, value) 
    for row, instrument in enumerate(instruments) 
        for col, value in enumerate(instrument)]

Not sure if it is any neater or more pythonic... But it is another way of expressing the loop.

One could argue that the list comprehension is clearer, since the "action" part (setValue) is placed first/at the top. Instead of being buried inside the loop.

EDIT:

Yet another way, using a helper function and a generator expression:

def loop(iterator):
    for item in iterator: pass

loop(self.table.setValue(row, col, value) 
    for row, instrument in enumerate(instruments) 
        for col, value in enumerate(instrument))

The helper function can be the built-in any() if you know for certain that the loop body will never evaluate to True in a boolean context (any() will stop the iteration on the first True).

Upvotes: -3

John Machin
John Machin

Reputation: 82934

What you are calling row is not a row, it is a row index. instrument is a row. Apart from that:

If the only tool that you have is a SetValue(row_index, column_index, value) method and that method does more than help replicate the structure of instruments laboriously, and the instruments collection is as your code describes, then there is no better way. It is already much better than

#WARNING: BAD PRACTICE! DON'T USE THIS CODE!
for row_index in xrange(len(instruments)):
    instrument = instruments[row_index]
    for column_index in xrange(len(instrument)):
        self.table.SetValue(row_index, column_index, instrument[column_index])

or any perversion thereof.

Otherwise you may be interested in import copy; self.table = copy.copy(instruments) or even (as SilentGhost has suggested) self.table = instruments

Upvotes: 4

jfs
jfs

Reputation: 414215

You've mentioned in the comments that this loop is a part of asynchronous function (in terms of twisted framework). In this case you don't want to block for a long time:

from twisted.internet import task

for i, row in enumerate(instruments):
    task.coiterate(self.table.SetValue(i, j, v) for j, v in enumerate(row))

Thus all rows are assigned in parallel.

NOTE:

  • Watch out for late binding for i and row. Use (lambda i=i, row=row: ...)() in that case.
  • task.coiterate() uses global object therefore there could be multiple table updates simultaneously (it might not be what you want).

Here's @SilentGhost' answer (deleted):

self.table = instruments

Because that's what you seem to be doing.

And the comment by @[Ben Hughes] I'm referring to:

I need to explicity call SetValue (its on a PyGridTableBase) for each value - as this code is invoked via a twisted deferred method - my brain is not much good at looping/enumeration in a neat way..... – Ben Hughes

Upvotes: 2

S.Lott
S.Lott

Reputation: 391854

The fundamental question is not regarding this loop.

The fundamental questions are these:

1) Where does this instruments structure come from, and why do you need to reorganize it?

2) What is this self.table structure on which you're calling SetValue?

3) What are you going to do with this self.table structure?

Until you answer these questions, your sample Python code has no context in which it can be evaluated. It's just code.

Upvotes: 2

Related Questions