【问题标题】:Safe unpack empty tuple array安全解包空元组数组
【发布时间】:2019-01-31 21:17:43
【问题描述】:

import re; print(re.findall("(.*) (.*)", "john smith")) 行输出[("john", "smith")],可以像[(first_name, last_name)] = re.findall(...) 一样解包。但是,在不匹配的情况下(findall 返回[]),此解包将引发ValueError: not enough values to unpack (expected 1, got 0)

安全地解压这个元组数组的正确方法是什么,它适用于匹配 ([("john", "smith")]) 和不匹配 ([]) 场景?

【问题讨论】:

  • if result: # unpacking code
  • findall 在您只期望(并且只能处理)一场比赛时是一个奇怪的选择。
  • @user2357112 我选择了它,因为它是可迭代的,而 search 不是
  • @user2357112 谢谢,这更有意义。我希望能够做类似first,last = re.search(r"(.*) (.*)", "john smith").groups(default=[None, None]) 之类的事情(由于search 返回None 而导致的不匹配场景错误)

标签: python python-3.x tuples iterable-unpacking


【解决方案1】:

一般的答案是在你跳跃之前先看看:

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

或请求原谅:

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

但是您实际上使用re.findall() 来查找单个结果会使事情变得过于复杂。使用re.seach() 并提取您的matched groups

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

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

【讨论】:

  • 任何方式使用re.search 作为一个衬里,也许使用@bihsing 的答案中的or 运算符?
  • @Frayt:是的,但是你是在打代码,而不是编写生产级 Python 代码。
  • @Frayt: 并且它需要您使用返回(None, None)group() 方法创建一个虚拟类,以使元组分配能够执行(re.search("(.*) (.*)", value) or dummy_object).groups()。这真的会损害代码库的可维护性。
  • 我同意虚拟对象对于单行来说是个坏主意,如果没有传播是我可以做的事情first, last = re.search(...)?.groups() or (None, None),我不会认为'代码打高尔夫球'
  • @Frayt: 对,但是 Python 中的 None 只是另一个对象(即使它是单例)而不是 NULL(即没有对象),所以 None-在 Python 中不会真正发生传播。
【解决方案2】:

没有;你必须明确检查返回值,看看是否有任何东西需要解压。

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

在 Python 3.8 中,您可以稍微压缩一下:

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

【讨论】:

    【解决方案3】:

    由于re.findall 在不匹配的情况下返回一个空列表,您可以使用or 运算符为first_namelast_name 分配默认值:

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

    【讨论】:

      【解决方案4】:

      这很糟糕,所以不要这样做,但你可以使用

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

      在不使用findall 的情况下做你想做的事情(这可能会返回多次点击,因此仍然失败,或者忽略空格,具体取决于您是否将. 限制为\S)。

      鉴于您的模式当前实际上匹配其中包含单个空格的任何内容(捕获最后一个空格之前的所有内容,以及之后的所有内容),避免 findall 不会给您带来太多好处,但如果您想实际排除包含以下内容的内容多个空格,或仅部分匹配的内容,您可以将. 切换为\S,也可能将search 切换为fullmatch

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

      无论哪种方式,它都滥用了一个不匹配返回None的事实,它没有groups方法,所以getattr可以在匹配上返回一个绑定的groups方法,或者一个lambda否则返回默认值。无论如何,您立即调用它,并根据需要获得groupslambda 的结果。

      再一次,不要这样做。这是合法的,只是丑陋(而且可能比任何合理的方法都慢)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-07
        • 2019-01-30
        • 2021-08-05
        • 1970-01-01
        • 2015-02-21
        • 1970-01-01
        • 2010-10-16
        • 2015-05-15
        相关资源
        最近更新 更多