Python 如何在列表理解中设置局部变量?
我有一个获取列表并返回对象的方法:Python 如何在列表理解中设置局部变量?,python,list-comprehension,Python,List Comprehension,我有一个获取列表并返回对象的方法: # input a list, returns an object def map_to_obj(lst): a_list = f(lst) return a_list[0] if a_list else None 我想得到一个包含所有映射元素的列表,这些元素不是None 像这样: v_list = [v1, v2, v3, v4] [map_to_obj(v) for v in v_list if map_to_obj(v)] 但是在列
# input a list, returns an object
def map_to_obj(lst):
a_list = f(lst)
return a_list[0] if a_list else None
我想得到一个包含所有映射元素的列表,这些元素不是None
像这样:
v_list = [v1, v2, v3, v4]
[map_to_obj(v) for v in v_list if map_to_obj(v)]
但是在列表理解中调用两次map\u to\u obj
方法似乎不太好
是否有一种方法可以在列表理解中使用局部变量,从而使其具有更好的性能
或者编译器会自动优化它吗
以下是我想要的:
(sml like)
[let mapped = map_to_obj(v) in for v in v_list if mapped end]
您可以使用python内置程序避免重新计算:
使用嵌套列表理解:
[x代表x在[map_to_obj(v)代表v在v_列表中]如果x]
或者更好的是,围绕生成器表达式的列表理解:
[x代表x in(映射到v对象(v)代表v in v_列表),如果x]
列表理解适用于简单情况,但有时简单的for
循环是最简单的解决方案:
other_list = []
for v in v_list:
obj = map_to_obj(v)
if obj:
other_list.append(obj)
现在,如果您确实想要一个列表comp,而不想构建一个tmp列表,那么您可以使用迭代器版本的filter
和map
:
import itertools as it
result = list(it.ifilter(None, it.imap(map_to_obj, v_list)))
或者更简单地说:
import itertools as it
result = filter(None, it.imap(map_to_obj, v_list)))
迭代器版本不构建临时列表,它们使用惰性求值。我找到了一种使用
reduce
的方法:
def map_and_append(lst, v):
mapped = map_to_obj(v)
if mapped is not None:
lst.append(mapped)
return lst
reduce(map_and_append, v_list, [])
这方面的性能如何?可以通过欺骗一点并使用额外的“for”来设置局部变量,该“for”通过包含局部变量所需值的1元素元组进行“迭代”。以下是使用此方法解决OP问题的方法:
[o for v in v_list for o in (map_to_obj(v),) if o]
这里,o
是为每个v
设置的等于map_to_obj(v)
的局部变量
在我的测试中,这比Liing Dog的嵌套生成器表达式稍微快一点(也比OP对
map\u to_obj(v)
的双重调用快一点),如果map\u to_obj
函数不太慢的话,它会比嵌套生成器表达式快一点。变量赋值只是一个单数绑定:
[x for v in l for x in [v]]
这是一个更一般的答案,也更接近你的建议。
因此,对于您的问题,您可以写:
[x for v in v_list for x in [map_to_obj(v)] if x]
从Python 3.8开始,引入(
:=
运算符),可以在列表理解中使用局部变量,以避免调用两次相同的函数:
在我们的例子中,我们可以将map_to_obj(v)
的求值命名为变量o
,同时使用表达式的结果过滤列表;因此,使用o
作为映射值:
[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]
有一个迭代解决方案吗?@HaoTan不在python 3中;在Python3中,
map
返回一个map对象,而不是列表,filter
返回一个filter对象;因此,这将在不制作中间列表的情况下链接函数。是否可以使用reduce
来完成此工作?@HaoTan:为什么reduce
用于将多个内容组合成一个内容,而不是将多个内容更改为多个内容。成对的map
和filter
(即list
ified)所做的工作相当于同时执行过滤和映射操作的listcomp所做的工作。这是一个很好的答案,当然与的答案相同,列表理解取代了map
和filter
。。。我会投票,因为我喜欢用l-c翻译的过滤器,但OP可能会批准其中任何一个答案,因为这两个答案都是好的、有用的答案。内在理解应该是一个生成器表达式。不需要建立整个列表,然后扔掉空的项目来构建另一个列表。@提姆的建议听起来很有意义,请考虑编辑你的答案来反映他的建议。@考伯特:如果是<代码> lambda < /Cord>(实际上,任何Python实现的函数都可以内联,以避免调用开销)是的,listcomp赢了。map
/filter
如果回调函数是C内置的,则可以赢,因为它们可以将所有工作推送到C层,绕过listcomp无法避免的字节码解释器开销,因为它们是Python级别的函数,只需直接支持附加到列表步骤的字节码。Sil举个例子:将behzad的答案改为用None替换lambda t:t不是None
。\uu_____
,尽管它的工作方式有点奇怪(对于其他None
s,它返回False
,而对于其他所有的则返回NotImplemented
,这恰好是真实的),将提高该解决方案的速度(在本地测试中,过滤器
工作下降近2倍)您可以使用timeit
模块为不同的解决方案计时,但您上面的代码片段是一种任意的过于复杂的方式,可以完成一件非常简单的事情-我怀疑它会比普通的for循环或filter/imap解决方案更快或更节省空间…@Bruno我喜欢y我们的“过度复杂化”!如果我看一下没有itertools的答案,我敢说性能是最能表达意图的,因此也是最具可读性的。那么性能呢?我不知道您在f(lst)上花费了多少cpu
调用,但以这种或那种方式删除None
s不太可能改变整个画面。reduce
在这里真的不被调用。它的用途是将值列表减少为单个值(通常是将标量列表减少为单个标量),而不是将一个列表转换为另一个列表。这与我的回答不同,只是使用了一个单元素列表而不是一个单元素元组,我认为元组在列表不需要可变时优于列表。:-)
[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]