我有一个方法,它接受一个列表并返回一个对象:
# 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_to_obj
方法似乎并不好。
有没有办法在列表推导中使用局部变量,以便它可以获得更好的性能?
或者编译器是否自动对其进行优化?
这就是我想要的:
(sml like)
[let mapped = map_to_obj(v) in for v in v_list if mapped end]
答案 0 :(得分:50)
使用嵌套列表理解:
[x for x in [map_to_obj(v) for v in v_list] if x]
或更好的是,围绕生成器表达式的列表理解:
[x for x in (map_to_obj(v) for v in v_list) if x]
答案 1 :(得分:5)
变量分配只是单数绑定:
[x for v in l for x in [v]]
这是一个更笼统的答案,也更接近您的建议。 因此,对于您的问题,您可以编写:
[x for v in v_list for x in [map_to_obj(v)] if x]
答案 2 :(得分:4)
您可以使用python内置filter
:
list(filter(lambda t: t is not None, map(map_to_obj, v_list)))
答案 3 :(得分:3)
从Python 3.8
开始,并引入assignment expressions (PEP 572)(:=
运算符),可以在列表推导中使用局部变量,以避免调用同一函数两次: / p>
在我们的例子中,我们可以将map_to_obj(v)
的求值命名为变量o
,同时使用表达式的结果来过滤列表。并因此使用o
作为映射值:
[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]
答案 4 :(得分:2)
局部变量可以在理解中通过欺骗一点并使用额外的'for'来设置,该'for'“迭代”通过包含局部变量的期望值的1元素元组。以下是使用此方法解决OP问题的方法:
[o for v in v_list for o in (map_to_obj(v),) if o]
此处,o
是为每个map_to_obj(v)
设置的局部变量等于v
。
在我的测试中,这比Lying Dog的嵌套生成器表达式稍快(并且也比OP对map_to_obj(v)
的双重调用更快,令人惊讶的是,如果{{1},它可能比嵌套生成器表达式更快功能不是太慢了。)
答案 5 :(得分:1)
列表推导适用于简单的情况,但有时一个普通的旧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)))
迭代器版本不构建临时列表,它们使用延迟评估。
答案 6 :(得分:0)
我找到了一种使用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, [])
这个表现怎么样?