我怎么高效的过滤算值在Python名单的理解?
-
02-07-2019 - |
题
蟒蛇列表理解的语法使得它很容易过滤器值内的理解。例如:
result = [x**2 for x in mylist if type(x) is int]
将返回的一个列表中的方块整数列表.然而,如果试验包括一些(代价高昂)计算和你想要过滤器的结果?一种选择是:
result = [expensive(x) for x in mylist if expensive(x)]
这将导致在一个列表中的非"虚假的"贵(x)值,但是昂贵()被称为两次,每个x。是有一个理解的语法可以让你做这个测试的话,只叫昂贵的每一次x?
解决方案
如果计算已经很好地捆绑到函数中,那么如何使用 filter
和 map
?
result = filter (None, map (expensive, mylist))
如果列表非常大,您可以使用 itertools.imap
。
其他提示
经过一分钟的思考后得出了我自己的答案。可以使用嵌套的理解来完成:
result = [y for y in (expensive(x) for x in mylist) if y]
我猜这有效,但我发现嵌套的理解只是边缘可读
最明显的(我认为最可读的)答案是不使用列表推导或生成器表达式,而是使用真正的生成器:
def gen_expensive(mylist):
for item in mylist:
result = expensive(item)
if result:
yield result
它需要更多的水平空间,但是一眼就能看到它的功能更容易,最终你不会重复自己。
result = [x for x in map(expensive,mylist) if x]
map()将返回传递给expensive()的mylist中每个对象的值列表。然后你可以列出 - 理解它,并丢弃不必要的值。
这有点像嵌套的理解,但应该更快(因为python解释器可以相当容易地优化它)。
这正是发电机适合处理的问题:
result = (expensive(x) for x in mylist)
result = (do_something(x) for x in result if some_condition(x))
...
result = [x for x in result if x] # finally, a list
- 这使得在管道的每个阶段都发生了什么。
- 隐式隐式
- 在最后一步使用发生器到处都是,所以没有大的中间列表 醇>
您可以随时记住 expensive()
功能,第二次调用它只是查找 x
的计算值。
你可以记住昂贵的(x)(如果你经常调用昂贵的(x),你可能应该以任何方式记住它。这个页面给出了python的memoize实现:
http://code.activestate.com/recipes/52201/
这有一个额外的好处,即昂贵的(x)可以运行少而不是N次,因为任何重复的条目都将使用前一次执行的备忘录。
请注意,这假设昂贵(x)是真正的函数,并且不依赖于可能更改的外部状态。如果昂贵(x)确实取决于外部状态,并且您可以检测到该状态发生变化,或者您知道在列表理解期间不会更改,那么您可以在理解之前重置备忘录。
我将具有优先:
itertools.ifilter(bool, (expensive(x) for x in mylist))
这具有的优点:
- 避免没有为功能(将被消除Python3): http://bugs.python.org/issue2186
- 只使用迭代器。
for
循环的普通旧用法也可以附加到列表中:
result = []
for x in mylist:
expense = expensive(x)
if expense:
result.append(expense)