views:

207

answers:

5
+4  Q: 

Learning Python

Hi all, Was coding something in Python. Have a piece of code, wanted to know if it can be done more elegantly...

# Statistics format is - done|remaining|200's|404's|size
statf = open(STATS_FILE, 'r').read()
starf = statf.strip().split('|')
done  = int(starf[0])
rema  = int(starf[1])
succ  = int(starf[2])
fails = int(starf[3])
size  = int(starf[4])
...

This goes on. I wanted to know if after splitting the line into a list, is there any better way to assign each list into a var. I have close to 30 lines assigning index values to vars. Just trying to learn more about Python that's it...

+6  A: 
done, rema, succ, fails, size, ... = [int(x) for x in starf]

Better:

labels = ("done", "rema", "succ", "fails", "size")

data = dict(zip(labels, [int(x) for x in starf]))

print data['done']
leoluk
if there's 30 items, I would say that's a less elegant :P
townsean
I like the new and improved version...now that's more elegant :)
townsean
+5  A: 

What I don't like about the answers so far is that they stick everything in one expression. You want to reduce the redundancy in your code, without doing too much at once.

If all of the items on the line are ints, then convert them all together, so you don't have to write int(...) each time:

starf = [int(i) for i in starf]

If only certain items are ints--maybe some are strings or floats--then you can convert just those:

for i in 0,1,2,3,4:
    starf[i] = int(starf[i]))

Assigning in blocks is useful; if you have many items--you said you had 30--you can split it up:

done, rema, succ = starf[0:2]
fails, size = starf[3:4]
Glenn Maynard
+4  A: 

I might use the csv module with a separator of | (though that might be overkill if you're "sure" the format will always be super-simple, single-line, no-strings, etc, etc). Like your low-level string processing, the csv reader will give you strings, and you'll need to call int on each (with a list comprehension or a map call) to get integers. Other tips include using the with statement to open your file, to ensure it won't cause a "file descriptor leak" (not indispensable in current CPython version, but an excellent idea for portability and future-proofing).

But I question the need for 30 separate barenames to represent 30 related values. Why not, for example, make a collections.NamedTuple type with appropriately-named fields, and initialize an instance thereof, then use qualified names for the fields, i.e., a nice namespace? Remember the last koan in the Zen of Python (import this at the interpreter prompt): "Namespaces are one honking great idea -- let's do more of those!"... barenames have their (limited;-) place, but representing dozens of related values is not one -- rather, this situation "cries out" for the "let's do more of those" approach (i.e., add one appropriate namespace grouping the related fields -- a much better way to organize your data).

Alex Martelli
yay for mentioning the Zen of Python (...and, unrelated to the discussion (sorry) but, your Python in A Nutshell book is one of my favorites :3 )
townsean
A: 

Using a Python dict is probably the most elegant choice.

If you put your keys in a list as such:

keys = ("done", "rema", "succ" ... )
somedict = dict(zip(keys, [int(v) for v in values]))

That would work. :-) Looks better than 30 lines too :-)

EDIT: I think there are dict comphrensions now, so that may look even better too! :-)
EDIT Part 2: Also, for the keys collection, you'd want to break that into multpile lines.
EDIT Again: fixed buggy part :)

townsean
I think it won't work... if you have two for in a comprehension list it makes a carthesian product of the two collections.
Chris
ah! crap, I did screw up there. *goes back to fixin'*
townsean
A: 

Thanks for all the answers. So here's the summary -

  1. Glenn's answer was to handle this issue in blocks. i.e. done, rema, succ = starf[0:2] etc.
  2. Leoluk's approach was more short & sweet taking advantage of python's immensely powerful dict comprehensions.
  3. Alex's answer was more design oriented. Loved this approach. I know it should be done the way Alex suggested but lot of code re-factoring needs to take place for that. Not a good time to do it now.
  4. townsean - same as 2

I have taken up Leoluk's approach. I am not sure what the speed implication for this is? I have no idea if List/Dict comprehensions take a hit on speed of execution. But it reduces the size of my code considerable for now. I'll optimize when the need comes :) Going by - "Pre-mature optimization is the root of all evil"...

MovieYoda
@Srikar: Since you have taken up Leoluk's approach it would be a good idea to follow the Stack Overflow custom of declaring him the winner of the joust. The procedure is simple: say his name out aloud and click on the tick mark next to his answer. When the tick mark turns green, SO will herald Leoluk's win to the rest of the world.
Manoj Govindan
yupp! just did that :)
MovieYoda
@Srikar: I heard the heralds :)
Manoj Govindan