Safe unpack empty tuple array

依然范特西╮ 提交于 2021-02-05 11:21:12

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!