You are not logged in.
"line" could have any number of words, but I want to unpack it into 4 vars regardless. I can just do "a, b, c, d = line.split" in Ruby, but in Python this is the only way I know how:
#!/usr/bin/env python
line = "arch linux moo"
words = line.split() + 4 * [None]
a, b, c, d = words[:4]
Edit for clarity: Basically I'm looking for a cleaner way to assign a list of 3 or fewer objects into 4 vars. Is the intermediary "words" list necessary here or is there a simpler solution? Thanks.
Solution: I can use *args and **kwargs to pass these variable-size argument lists. *args and **kwargs will unpack positional and keyword arguments so that I don't need to unpack them manually.
Last edited by tdy (2009-12-21 18:19:27)
Offline
>>> "one two three four five".split(" ", 3)
['one', 'two', 'three', 'four five']
M*cr*s*ft: Who needs quality when you have marketing?
Offline
Thanks pointone, unfortunately that doesn't deal with the crux of the problem. 4+ words isn't the issue, it's <4.
>>> a, b, c, d = "one two three".split(" ", 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 3 values to unpack
That's why my code snippet manually pads the list with 4 extra null objects. Basically I'm wondering if there's a cleaner way to assign a list of 3 (or fewer) objects into 4 vars.
Last edited by tdy (2009-12-19 06:17:24)
Offline
I don't know of any other way of doing it, tdy. I would normally write it on one line as:
>>> line = "arch linux moo"
>>> (line.split() + [None] * 4)[:4]
['arch', 'linux', 'moo', None]
If you were doing this a lot perhaps you could make it a function taking a list and a desired length and returning a list either truncated or extended to the desired length.
Offline
Couldn't you also run a check on the list length using "len", and then if len(line) <4, pad it, and otherwise just leave it be? Think of it as error-handling. Or else you could always do len(line)/4, and then use that int as the number of variables (unless that's unacceptable for the script you have in mind).
Just kind of thinking out loud, but I hope that helps,
Lswest
Lswest <- the first letter of my username is a lowercase "L".
"...the Linux philosophy is "laugh in the face of danger". Oops. Wrong one. "Do it yourself". That's it." - Linus Torvalds
Offline
line = "ga bu zo"
vars = [None] * 4
for (i, x) in enumerate(line.split(" ", 3)):
vars[i] = x
[a,b,c,d] = vars
Looks cleaner for me.
(Still much longer than the original Ruby one-liner)
EDIT: add the split(" ",3) to be sure it also works if line has more than 4 words.
Last edited by Yannick_LM (2009-12-19 15:03:30)
Offline
Can we ask why you want to do this? Is it just an academic exercise to see if you can get a python solution that's a succinct as the ruby? If you're needing to do this in an actual program or script then there's almost certainly a more pythonic way.
Anyway, this is probably what I'd end up doing if I was to do it at all. It's a little bit longer than what you originally had but I think it's easier to read.
line = "spam eggs ham".split()
for i in 'abcd': locals()[i] = line.pop(0) if line else None
Last edited by AlecSchueler (2009-12-20 03:47:36)
Offline
Can we ask why you want to do this? Is it just an academic exercise to see if you can get a python solution that's a succinct as the ruby?
I'm parsing a config file where each line can have a varying number of fields. The Ruby code was just a simple, clear example of what I want to do. I assure you this isn't homework. In fact, it would be a pretty cruel exercise considering it seems to be impossible based on the responses in this post...
EDIT: add the split(" ",3) to be sure it also works if line has more than 4 words.
I prefer to splice the first 4 words instead of combining all the words past 4.
Thanks for all the responses. Since there doesn't seem to be way to do a basic assignment statement, I'll go from there and figure out how I want to parse it.
Offline
Sorry about that. I didn't mean to suggest you were asking for homework help. The term 'academic exercise' was perhaps a poor choice of mine; I meant it more as a personal kind of exercise, for your private study.
Could there be a way to do it without any variable assignment at all? What are you doing with a, b, c and d next?
Offline
If you know that the words will always be separated by spaces, then maybe you could use this:
#!/usr/bin/env python
line = "arch linux moo"
a, b, c, d = (line + " ").split(' ')
*edit*
I just realized that you probably want "None" instead of an empty string, in which case mikesd's one-liner seems to be the best solution. Sorry for the noise.
Last edited by Xyne (2009-12-20 06:18:18)
My Arch Linux Stuff • Forum Etiquette • Community Ethos - Arch is not for everyone
Offline
I'm parsing a config file where each line can have a varying number of fields. The Ruby code was just a simple, clear example of what I want to do. I assure you this isn't homework. In fact, it would be a pretty cruel exercise considering it seems to be impossible based on the responses in this post...
In the case of parsing a config file I would structure my parser to simply convert each line to a list of 1, 2, 3 or 4 items depending on the line. The length of the list may be handy to work out what sort of line I have and I wouldn't need to worry about checking for None anywhere. This of course assumes each line is independent. Passing a list around would be cleaner than using 4 independent variables where some of them may, or may not, be None. Just my 2 cents.
Offline
If you're parsing a conf-file, why force a line which may only have three fields to have four? Why wouldn't you leave it as a list of N fields in your parsing code, then check the number of items in the list when it comes time for the arguments to be interpreted?
I apologize if I've misunderstood you here, but it looks like you're trying to solve this problem (whatever it is) in an unpythonic way.
Edited for clarity
Last edited by Trent (2009-12-20 17:16:01)
Offline
If you're parsing a conf-file, why force a line which may only have three fields to have four? Why wouldn't you leave it as a list of N fields in your parsing code, then check the number of items in the list when it comes time for the arguments to be interpreted?
I apologize if I've misunderstood you here, but it looks like you're trying to solve this problem (whatever it is) in an unpythonic way.
Edited for clarity
I agree completely. I know this isn't exactly helpful for you, but this just seems like the wrong solution. There are certainly languages that promote a solution like this - but Python isn't one of them.
Offline
This has gone a bit off track. Perhaps I shouldn't have called it a config file since I think that was the main culprit. This is in no way just a regular config file where I'm just saving config variables, like "some_setting=foo" or "ignore=moo,bar,blah" or something. I have a whole Parser class dedicated to performing the operations/substitutions/etc needed to parse the "config" file's individual lines.
If you're parsing a conf-file, why force a line which may only have three fields to have four? Why wouldn't you leave it as a list of N fields in your parsing code, then check the number of items in the list when it comes time for the arguments to be interpreted?
That's not really the point of my post. That is just the direction people took it. My main question was more like "this is a really simple statement in Ruby, can I do this in Python?" Since it's apparently not a simple statement, I may just rethink how I set up my class. In Ruby, I would simply unpack the directives like the code in my first post and then initialize my Parser object with those vars. The optional fields would just be initialized to nil automatically if they weren't being used.
My original question was basically just to make sure that there wasn't some module or function I hadn't learned of yet to do that equivalent statement in Python.
Offline
Here's what I see:
class UsefulData:
def __init__(a = None, b = None, c = None, d = None):
self.a, self.b, self.c, self.d = a, b, c, d
fields = "foo bar baz" # may have 0, 1, 2, 3, or 4 tokens
data = UsefulData(*fields.split())
My main question was more like "this is a really simple statement in Ruby, can I do this in Python?" Since it's apparently not a simple statement, I may just rethink how I set up my class.
Exactly what I was saying: if you have to resort to tricks like this, it probably means you're thinking about the problem in the wrong way.
Last edited by Trent (2009-12-21 14:45:17)
Offline
Here's what I see:
class UsefulData: def __init__(a = None, b = None, c = None, d = None): self.a, self.b, self.c, self.d = a, b, c, d fields = "foo bar baz" # may have 0, 1, 2, 3, or 4 tokens data = UsefulData(fields.split())
fields.split() returns a list. All you're doing is setting a to ['foo', 'bar', 'baz'], b to None, c to None, and d to None.
Actually you basically just illustrated the point of my original post. The fact that str.split() returns a list is my main headache in setting up a clean constructor.
But as I said, I'm aware there are plenty of ways to do it. My post was just to make sure that there wasn't some module or function that I was unaware of that could do what I was hoping for.
Last edited by tdy (2009-12-21 05:13:00)
Offline
I suppose there's not much point in saying it now, but you could use this to send each element of the list as a separate argument.
data = UsefulData(*fields.split())
Offline
I suppose there's not much point in saying it now, but you could use this to send each element of the list as a separate argument.
data = UsefulData(*fields.split())
Thanks... that was what I meant to do. (Edited my first post.)
Offline
oops, double post
Last edited by Trent (2009-12-21 14:45:45)
Offline
I suppose there's not much point in saying it now, but you could use this to send each element of the list as a separate argument.
data = UsefulData(*fields.split())
Yes! That will work perfectly. I am now aware of the *args and **kwargs operators. Thanks everyone.
Last edited by tdy (2009-12-21 17:48:48)
Offline