如何在列表推导式中设置局部变量?
我有一个方法,它接收一个列表并返回一个对象:
# 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]
8 个回答
5
在一个列表推导式中,我们可以通过稍微“作弊”一下,使用一个额外的'for'循环来设置局部变量。这个'for'循环其实是在遍历一个只包含一个元素的元组,这个元素就是我们想要的局部变量的值。下面是用这种方法解决问题的一个例子:
[o for v in v_list for o in (map_to_obj(v),) if o]
在这里,o
是我们设置的局部变量,它的值等于每个v
经过map_to_obj(v)
处理后的结果。
根据我的测试,这种方法比Lying Dog的嵌套生成器表达式稍微快一点(而且也比OP的双重调用map_to_obj(v)
快,令人惊讶的是,如果map_to_obj
函数不是太慢,这种方法可能会比嵌套生成器表达式更快)。
7
你可以通过使用Python内置的filter
来避免重复计算:
list(filter(lambda t: t is not None, map(map_to_obj, v_list)))
11
变量赋值其实就是把一个值绑定到一个名字上:
[x for v in l for x in [v]]
这个回答更通用,也更接近你提出的问题。 所以针对你的问题,你可以这样写:
[x for v in v_list for x in [map_to_obj(v)] if x]
78
可以使用嵌套的列表推导式:
[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]
92
从 Python 3.8
开始,引入了 赋值表达式 (PEP 572),也就是 :=
这个符号。这个新特性让我们可以在列表推导式中使用一个局部变量,这样就能避免重复调用同一个函数。
在我们的例子中,我们可以把 map_to_obj(v)
的结果命名为一个变量 o
,同时用这个结果来过滤列表;这样就可以把 o
当作映射后的值来使用:
[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]