tags:

views:

1097

answers:

10

Hello. In python, assignment operator can unpack list or tuple into variables, like this:

l = (1, 2)
a, b = l # here goes auto unpack

But i need to specify exactly same amount of names to the left as an items count in the list to the right. But sometimes i don't know a size of the list to the right, for example if i use split(). Example:

a, b = "length=25".split("=") # this will result in a="length" and b=25

But the following code will lead an error:

a, b = "DEFAULT_LENGTH".split("=") # error, list has only 1 item

is it possible to somehow unpack list in the example above so i get a = "DEFAULT_LENGTH" and b equals to 'None' or not set? Straightforward way looks kinda long:

a = b = None
if "=" in string :
  a, b = string.split("=")
else :
  a = string
A: 

Have you tried this?

values = aString.split("=")
if len(values) == 1:
   a = values[0]
else:
   a, b = values
S.Lott
It should be a = values[0]
Devin Jeanpierre
or "a, = values"
MizardX
A: 

This is slightly better than your solution but still not very elegant; it wouldn't surprise me if there's a better way to do it.

a, b = (string.split("=") + [None])[:2]
Adam Rosenfield
+13  A: 
# this will result in a="length" and b="25"
a, b = "length=25".partition("=")[::2]

# this will result in a="DEFAULT_LENGTH" and b=""
a, b = "DEFAULT_LENGTH".partition("=")[::2]
Chris Upchurch
any way to skip 'equals' variable name, eg a,,b = str.partition("=") ?
Eye of Hell
@Eye: you can use [::2] to select elements with even index
SilentGhost
Nice SilentGhost! Added it to the answer.
Chris Upchurch
[::2] is brilliant, thanks!
Eye of Hell
+2  A: 

You could write a helper function to do it.

>>> def pack(values, size):
...     if len(values) >= size:
...         return values[:size]
...     return values + [None] * (size - len(values))
...
>>> a, b = pack('a:b:c'.split(':'), 2)
>>> a, b
('a', 'b')
>>> a, b = pack('a'.split(':'), 2)
>>> a, b
('a', None)
FogleBird
A: 

Don't use this code, it is meant as a joke, but it does what you want:

a = b = None
try: a, b = [a for a in 'DEFAULT_LENGTH'.split('=')]
except: pass
RossFabricant
yeah, i can list comprehensions too :).
Eye of Hell
Just wait till someone tries to extend it to work for 3 variables though (or use python3)! Putting that in your code someone might read would be rather evil :-) A more sane approach is possibly just putting a=theString in the except block.
Brian
+3  A: 

The nicest way is using the partition string method:

Split the string at the first occurrence of sep, and return a 3-tuple containing the part before the separator, the separator itself, and the part after the separator. If the separator is not found, return a 3-tuple containing the string itself, followed by two empty strings.

New in version 2.5.

>>> inputstr = "length=25"
>>> inputstr.partition("=")
('length', '=', '25')
>>> name, _, value = inputstr.partition("=")
>>> print name, value
length 25

It also works for strings not containing the =:

>>> inputstr = "DEFAULT_VALUE"
>>> inputstr.partition("=")
('DEFAULT_VALUE', '', '')

If for some reason you are using a version of Python before 2.5, you can use list-slicing to do much the same, if slightly less tidily:

>>> x = "DEFAULT_LENGTH"

>>> a = x.split("=")[0]
>>> b = "=".join(x.split("=")[1:])

>>> print (a, b)
('DEFAULT_LENGTH', '')

..and when x = "length=25":

('length', '25')

Easily turned into a function or lambda:

>>> part = lambda x: (x.split("=")[0], "=".join(x.split("=")[1:]))
>>> part("length=25")
('length', '25')
>>> part('DEFAULT_LENGTH')
('DEFAULT_LENGTH', '')
dbr
+1 for str.partition
gorsky
+10  A: 

This may be of no use to you unless you're using python 3. However, for completeness, it's worth noting that the extended tuple unpacking introduced there allows you do do things like:

>>> a, *b = "length=25".split("=")
>>> a,b
("length", ['25'])
>>> a, *b = "DEFAULT_LENGTH".split("=")
>>> a,b
("DEFAULT_LENGTH", [])

ie. tuple unpacking now works similarly to how it does in argument unpacking, so you can denote "the rest of the items" with *, and get them as a (possibly empty) list.

Partition is probably the best solution for what you're doing however.

Brian
A: 

But sometimes i don't know a size of the list to the right, for example if i use split().

Yeah, when I've got cases with limit>1 (so I can't use partition) I usually plump for:

def paddedsplit(s, find, limit):
    parts= s.split(find, limit)
    return parts+[parts[0][:0]]*(limit+1-len(parts))

username, password, hash= paddedsplit(credentials, ':', 2)

(parts[0][:0] is there to get an empty ‘str’ or ‘unicode’, matching whichever of those the split produced. You could use None if you prefer.)

bobince
A: 

Many other solutions have been proposed, but I have to say the most straightforward to me is still

a, b = string.split("=") if "=" in string else (string, None)
dF
A: 

As an alternative, perhaps use a regular expression?

>>> import re
>>> unpack_re = re.compile("(\w*)(?:=(\w*))?")

>>> x = "DEFAULT_LENGTH"
>>> unpack_re.match(x).groups()
('DEFAULT_LENGTH', None)

>>> y = "length=107"
>>> unpack_re.match(y).groups()
('length', '107')

If you make sure the re.match() always succeeds, .groups() will always return the right number of elements to unpack into your tuple, so you can safely do

a,b = unpack_re.match(x).groups()
Sharkey