问题
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 the event of a non-match (findall
returning []
) this unpacking throws ValueError: not enough values to unpack (expected 1, got 0)
.
What is the correct way to safely unpack this array of tuples, which would work in both match ([("john", "smith")]
) and non-match ([]
) scenarios?
回答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
回答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
回答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)]
回答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).
来源:https://stackoverflow.com/questions/54469369/safe-unpack-empty-tuple-array