views:

233

answers:

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

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

codeape
it's certainly is less readable.
SilentGhost
Do you think so? The statements are exactly the same, only ordered differently, and wrapped in []
codeape
That being said, I've never used a list comprehension in this way myself. It feels a bit weird to not have ``variable = [...]``, only the ``[...]`` without using the returned value.
codeape
It's certainly a good way of uselessly filling up a list with a whole series of None values and then throwing the list away.
John Machin
Sure. To avoid that, use a generator expression
codeape
@codeape: After your edit, you are STILL abusing the syntax which is: `expression "for" ...` not `statement "for" ...`
John Machin
self.table.setValue() is also an expression. Two attribute references and a call.
codeape
@John Machin: Note how the accepted answer is also "abusing" the generator expression syntax :-)
codeape
@codeape: Problem is that the "expression" returns None; it is being evaluated for its side effects. You're right about the accepted answer; I saw it mentioned `twisted`, shuddered and stopped reading ;-)
John Machin
+4  A: 

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

John Machin
+2  A: 

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.

S.Lott
+1  A: 

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

J.F. Sebastian
As I write this comment, there are no comments by the questioner, neither on his question nor on any of the existing answers. I note in this context that SilentGhost has deleted his original answer :-). It would be a very good idea if the questioner updated his question with any relevant information.
John Machin
J.F. Sebastian
Thanks for the clarification. SO really needs an alternative to deleting messages -- an "I wish I'd never written this" button :-) -- apart from problems like leaving other answers referring to thin air, I have noticed cases where an answer that acquired upvotes in the early hours of its life but was later shown to be incorrect should have its reputation points surrendered but the text left there because there are references to it in other answers.
John Machin