如果只有一个结果,列表推导的替代方案
我开始慢慢习惯在Python中使用列表推导式,但我有点担心自己用得不太对。有几次我遇到这样的情况:我用列表推导式生成了一个列表,但接着立刻取出了这个列表中的第一个(也是唯一的)项目。下面是一个例子:
actor = [actor for actor in self.actors if actor.name==actorName][0]
(self.actors 是一个对象列表,我想从中找到一个特定名字(字符串)的对象,这个名字在 actorName 里。)
我想从这个列表中提取出符合我需要的参数的对象。这样做合理吗?看到那个 [0] 我有点不安。
4 个回答
这篇文章提到了一种自定义的 find()
函数,效果非常不错。还有一个评论者分享了一个基于生成器的方法。总的来说,似乎没有一种特别完美的方法来解决这个问题,但这些解决方案也都还不错。
如果你想从可能有很多匹配项中获取第一个匹配,使用 next(...)
是个不错的选择。
但如果你只期待有一个匹配,建议你写得更稳妥一些:
[actor] = [actor for actor in self.actors if actor.name==actorName]
这个方法总是会扫描到最后,但和直接用 [0]
不同的是,使用 解构赋值 赋值给 [actor]
时,如果没有匹配项或者匹配项超过一个,就会抛出一个值错误(ValueError)。
这不仅能帮助你捕捉到错误,更重要的是,它能向阅读代码的人清楚地传达你的假设。
如果你想要在没有匹配项时使用默认值,但仍然想捕捉到超过一个匹配的情况,可以这样做:
[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)
(无论如何,左边的列表和元组语法只是外观上的区别,不会影响行为)
你可以使用生成器表达式和 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)