简体   繁体   English

如果只有一个结果,则替代列表理解

[英]Alternative to list comprehension if there will be only one result

I'm starting to get used to list comprehension in Python but I'm afraid I'm using it somewhat improperly.我开始习惯在 Python 中列出理解,但恐怕我使用它有点不正确。 I've run into a scenario a few times where I'm using list comprehension but immediately taking the first (and only) item from the list that is generated.我曾经遇到过几次使用列表理解但立即从生成的列表中获取第一个(也是唯一一个)项目的场景。 Here is an example:这是一个例子:

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

(self.actors contains a list of objects and I'm trying to get to the one with a specific (string) name, which is in actorName.) (self.actors 包含一个对象列表,我试图找到一个具有特定(字符串)名称的对象,该名称位于 actorName 中。)

I'm trying to pull out the object from the list that matches the parameter I'm looking for.我正在尝试从与我要查找的参数匹配的列表中拉出 object。 Is this method unreasonable?这种方法不合理吗? The dangling [0] makes me feel a bit insecure.悬空的 [0] 让我感到有点不安全。

You could use a generator expression and next instead.您可以使用生成器表达式和next代替。 This would be more efficient as well, since an intermediate list is not created and iteration can stop once a match has been found:这也会更有效,因为没有创建中间列表,并且一旦找到匹配项就可以停止迭代:

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

And as senderle points out, another advantage to this approach is that you can specify a default if no match is found:正如senderle指出的那样,这种方法的另一个优点是,如果找不到匹配项,您可以指定默认值:

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

If you want to take the first match of potentially many, next(...) is great.如果您想参加可能很多人的第一场比赛,那么next(...)很棒。 But if you expect exactly one, consider writing it defensively:但是,如果您期望恰好有一个,请考虑防御性地编写它:

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

This always scans to the end, but unlike [0] , the destructuring assignment into [actor] throws a ValueError if there are 0 or more than one match.这总是扫描到最后,但与[0]不同的是,如果有 0 个或多个匹配项,则对[actor]解构赋值会引发 ValueError 。 Perhaps even more important then catching bugs, this communicates your assumption to the reader.也许比捕捉错误更重要的是,这会将您的假设传达给读者。

If you want a default for 0 matches, but still catch >1 matches:如果您想要 0 个匹配项的默认值,但仍捕获 >1 个匹配项:

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

PS it's also possible to use a generator expression on right side: PS也可以在右侧使用生成器表达式:

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

which may be a tiny bit more efficient (?).这可能会更有效(?)。 You could also use tuple syntax on the left side — looks more symmetric but the comma is ugly and too easy to miss IMHO:你也可以在左边使用元组语法——看起来更对称,但逗号很难看,而且很容易错过恕我直言:

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

(anyway list vs tuple syntax on left side is purely cosmetic doesn't affect behavior) (无论如何左侧的列表与元组语法纯粹是装饰性的,不会影响行为)

This post has a custom find() function which works quite well, and a commenter there also linked to this method based on generators . 这篇文章有一个自定义的find() function 效果很好,并且那里的评论者也链接到这个基于 generators 的方法 Basically, it sounds like there's no single great way to do this — but these solutions aren't bad.基本上,听起来没有单一的好方法可以做到这一点——但这些解决方案还不错。

Personally I'd to this in a proper loop.就我个人而言,我会在适当的循环中做到这一点。

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

It's quite a bit longer, but it does have the advantage that it stops looping as soon as a match is found.它要长一些,但它确实具有一旦找到匹配项就停止循环的优点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM