Skip to content Skip to sidebar Skip to footer

Safe Unpack Empty Tuple Array

The line import re; print(re.findall('(.*) (.*)', 'john smith')) outputs [('john', 'smith')], which can be unpacked like [(first_name, last_name)] = re.findall(...). However, in th

Solution 1:

The generic answer is to look before you leap:

if result:
    [(first_name, last_name)] = result

or to ask for forgiveness:

try:
    [(first_name, last_name)] = result
except ValueError:
    pass

but you are actually overcomplicating things by using re.findall() to find a single result. Use re.seach() and extract your matched groups:

match = re.search("(.*) (.*)", value)
if match:
    firstname, lastname = match.groups()

or

try:
    firstname, lastname = re.search("(.*) (.*)", value).groups()
except AttributeError:
    # An attribute error is raised when `re.search()` returned None
    pass

Solution 2:

There isn't one; you have to explicitly check the return value to see if there is, in fact, anything to unpack.

x = re.findall(...)
if x:
    [(first_name, last_name)] = x

In Python 3.8, you'll be able to compact this slightly:

if x := re.findall(...):
    [(first_name, last_name)] = x

Solution 3:

Since re.findall returns an empty list in the event of a non-match, you can use the or operator to assign default values to first_name and last_name instead:

[(first_name, last_name)] = re.findall("(.*) (.*)", "johnsmith") or [(None, None)]

Solution 4:

This is terrible, so don't do it, but you could use

first, last = getattr(re.search(r"(.*) (.*)", "john smith"), 'groups', lambda: (None, None))()

to do what you want as a one-liner without using findall (which could return multiple hits, and so still fail, or ignore spaces depending on whether you limit the . to \S).

Given your pattern current matches literally anything with a single space in it (capturing everything before the final space, and everything after it), avoid findall doesn't gain you much, but if you want to actually exclude stuff with more than one space, or things that match only partially, you could switch the . to \S, and possibly search to fullmatch:

first, last = getattr(re.fullmatch(r"(\S*) (\S*)", "john smith"), 'groups', lambda: (None, None))()

Either way, it abuses the fact that a non-match returns None, which has no groups method, so getattr can return a bound groups method on a match, or a lambda that returns the defaults otherwise. Regardless, you immediately call it, and get the result of groups or the lambda as appropriate.

Again, don't do this. It's legal, it's just ugly (and likely slower than any reasonable method).


Post a Comment for "Safe Unpack Empty Tuple Array"