How to reliably split a string in Python if it cannot contain a template or all n elements?

In Perl, I can do:

my ($x, $y) = split /:/, $str; 

And it will work whether the string will contain a pattern.

In Python, however, this will not work:

 a, b = "foo".split(":") # ValueError: not enough values to unpack 

What is the canonical way to prevent errors in such cases?

+73
python string split
Jul 01 '16 at 15:50
source share
5 answers

If you split into two parts (for example, in your example), you can use str.partition() to get a guaranteed argument that decompresses size 3:

 >>> a, sep, b = 'foo'.partition(':') >>> a, sep, b ('foo', '', '') 

str.partition() always returns a 3-tuple, regardless of whether a delimiter is found or not.

Another alternative for Python 3.x is to use extended iterative unpacking :

 >>> a, *b = 'foo'.split(':') >>> a, b ('foo', []) 

This assigns the first separator element a and the list of remaining elements (if any) to b .

+108
Jul 01 '16 at 15:52
source share

Since you are on Python 3, this is easy. PEP 3132 introduced a welcome simplification of syntax when assigning tuples - extended iterative unpacking. Previously, when assigning variables in a tuple, the number of elements to the left of the assignment must be exactly the same as the number to the right.

In Python 3, we can designate any variable on the left as a list by first specifying an asterisk *. This will capture as many values ​​as possible, while still filling in the variables on the right (so it should not be the rightmost element). This avoids many unpleasant fragments when we do not know the length of the tuple.

 a, *b = "foo".split(":") print("a:", a, "b:", b) 

gives:

 a: foo b: [] 

EDIT the following comments and discussions:

Compared to the Perl version, this is significantly different, but it is Python (3). Compared to the Perl version, re.split() will be more similar, however, accessing the RE mechanism to split around a single character is an extra overhead.

With a few elements in Python:

 s = 'hello:world:sailor' a, *b = s.split(":") print("a:", a, "b:", b) 

gives:

 a: hello b: ['world', 'sailor'] 

However, in Perl:

 my $s = 'hello:world:sailor'; my ($a, $b) = split /:/, $s; print "a: $ab: $b\n"; 

gives:

 a: hello b: world 

You can see that additional elements are ignored or lost in Perl. This is pretty easy to replicate in Python if required:

 s = 'hello:world:sailor' a, *b = s.split(":") b = b[0] print("a:", a, "b:", b) 

So the equivalent of a, *b = s.split(":") in Perl would be

 my ($a, @b) = split /:/, $s; 

NB: we should not use $a and $b in general Perl, since they are of particular importance when used with sort . I used them here for consistency with the Python example.

Python has an extra trick up its sleeve; we can unpack any element in the tuple on the left:

 s = "one:two:three:four" a, *b, c = s.split(':') print("a:", a, "b:", b, "c:", c) 

gives:

 a: one b: ['two', 'three'] c: four 

While in the Perl equivalent, the array ( @b ) is greedy, and the scalar $c is undef :

 use strict; use warnings; my $s = 'one:two:three:four'; my ($a, @b, $c) = split /:/, $s; print "a: $ab: @bc: $c\n"; 

gives:

 Use of uninitialized value $c in concatenation (.) or string at gash.pl line 8. a: one b: two three four c: 
+60
Jul 01 '16 at 16:01
source share

You can always catch an exception.

For example:

 some_string = "foo" try: a, b = some_string.split(":") except ValueError: a = some_string b = "" 

Assigning the entire source string a and the empty string to b is the desired behavior, I would probably use str.partition() , as eugene y suggests. However, this solution gives you more control over what happens when there is no separator in the string, which may be useful in some cases.

+22
Jul 01 '16 at 17:09
source share

split will always return a list. a, b = ... will always expect the length of the list to be two. You can use something like l = string.split(':'); a = l[0]; ... l = string.split(':'); a = l[0]; ... l = string.split(':'); a = l[0]; ...

Here is one liner: a, b = (string.split(':') + [None]*2)[:2]

+17
Jul 01 '16 at 16:12
source share

How to use regular expressions:

 import re string = 'one:two:three:four' 

in 3.X:

 a, *b = re.split(':', string) 

in 2.X:

 a, b = re.split(':', string)[0], re.split(':', string)[1:] 

This way you can also use regular expressions to separate (ie \ d)

+3
Jul 03 '16 at 6:12
source share



All Articles