【问题标题】:Alternative to list comprehension if there will be only one result如果只有一个结果,则替代列表理解
【发布时间】:2011-08-10 06:43:15
【问题描述】:

我开始习惯于在 Python 中列出推导式,但恐怕我使用它有些不当。我曾经遇到过几次使用列表理解但立即从生成的列表中获取第一个(也是唯一一个)项目的场景。这是一个例子:

actor = [actor for actor in self.actors if actor.name==actorName][0]

(self.actors 包含一个对象列表,我正在尝试找到一个具有特定(字符串)名称的对象,该名称位于 actorName 中。)

我正在尝试从列表中提取与我正在寻找的参数匹配的对象。这种方法不合理吗?悬空的 [0] 让我觉得有点不安全。

【问题讨论】:

  • 不要害怕,这不是可怕:)找到匹配项时停止循环的以下答案之一可能是更好的选择。 next(<generator_exp>) 还不错,但如果你尝试让它在 80 个字符行中工作,它会变得很丑。如果您要按名称查找一堆演员,最好创建一个dict,这样您就可以直接查找他们。
  • 幸运的是,这不是我经常或在大型场景中做的事情。我很擅长字典,并且会牢记这一技巧。

标签: python list-comprehension


【解决方案1】:

您可以使用生成器表达式和 next 来代替。这也会更有效,因为没有创建中间列表,并且一旦找到匹配项就可以停止迭代:

actor = next(actor for actor in self.actors if actor.name==actorName)

正如senderle 指出的那样,这种方法的另一个优点是,如果找不到匹配项,您可以指定默认值:

actor = next((actor for actor in self.actors if actor.name==actorName), None)

【讨论】:

  • 更高效——如果没有找到匹配项,则允许您指定默认值。
  • 谢谢。你能澄清一下“不需要消费”吗?这是否意味着一旦找到匹配项就会停止迭代?
  • 澄清这方面的措辞。
  • 谢谢。这绝对是有用的信息,并且明显优于我的方法。
  • 使用这些单行代码越来越爱 Python! :]
【解决方案2】:

如果您想参加可能很多人的第一场比赛,next(...) 很棒。 但是,如果您期望正好有一个,请考虑防御性地编写它:

[actor] = [actor for actor in self.actors if actor.name==actorName]

这总是扫描到最后,但与[0] 不同的是,destructuring assignment[actor] 会在有 0 个或多个匹配项时抛出 ValueError。 也许比捕捉错误更重要的是,这会将您的假设传达给读者。

如果您想要 0 个匹配项的默认值,但仍捕获 >1 个匹配项:

[actor] = [actor for actor in self.actors if actor.name==actorName] or [default]

附:也可以在右侧使用生成器表达式:

[actor] = (actor for actor in self.actors if actor.name==actorName)

这可能更有效(?)。你也可以在左边使用元组语法——看起来更对称,但逗号很难看,很容易错过恕我直言:

(actor,) = (actor for actor in self.actors if actor.name==actorName)
actor, = (actor for actor in self.actors if actor.name==actorName)

(无论如何左侧的列表与元组语法纯粹是装饰性的,不会影响行为)

【讨论】:

  • 您可能已经看到:[a, b] = [b, a] 用于交换,尽管它通常不带括号显示。它被称为“解构赋值”或“解包”。左侧的括号/父项是可选的,但对于单元素案例是必需的。右侧可以是任何 Python 表达式,在本例中是列表推导式。
  • 我明白了。感谢您的术语。我在其他情况下使用过打包/解包,但从未将它与单个变量一起使用来强制单个结果。
【解决方案3】:

This post 有一个自定义的find() 函数,效果很好,并且那里的评论者也链接到this method based on generators。基本上,听起来没有单一的好方法可以做到这一点——但这些解决方案还不错。

【讨论】:

  • 有趣的是有关于这个主题的博客文章。感谢您的概述,我很难找到有关此问题的信息。
【解决方案4】:

我个人会在适当的循环中这样做。

actor = None
for actor in self.actors:
    if actor.name == actorName:
        break

它有点长,但它的优点是一旦找到匹配项就停止循环。

【讨论】:

  • next() 解决方案也是如此。
猜你喜欢
  • 1970-01-01
  • 2019-11-23
  • 1970-01-01
  • 1970-01-01
  • 2019-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多