列表推导:对组件的引用
总的来说:我需要写一个列表推导式,但在这个推导式中我想引用正在创建的列表。
这可能不是你每天都需要做的事情,但我觉得也不算特别奇怪。
也许这里没有答案——不过请不要告诉我应该用for循环。虽然这样可能是对的,但对我没有帮助。原因是这个代码片段是一个ETL模块的一部分,所以性能很重要,同时我也想避免创建一个临时容器——这就是我想用列表推导式来写这一步的原因。如果用for循环能解决我的问题,我早就写一个了。
无论如何,我就是写不出这个特定的列表推导式。原因是:我需要写的表达式是这个样子的:
[ some_function(s) for s in raw_data if s not in this_list ]
在这个伪代码中,“this_list”指的是通过评估这个列表推导式而创建的列表。这就是我卡住的原因——因为这个列表在我的列表推导式被评估之前是不存在的,所以我不知道该怎么引用它。
到目前为止,我考虑过的事情(可能基于一个或多个错误的假设,虽然我不太确定具体在哪里):
难道Python解释器不需要给这个正在构建的列表起个名字吗?我觉得是的。
这个临时名字可能是从某个用于构建我列表的绑定方法中来的(比如'sum'?)
但即使我费心去找那个绑定方法,假设它确实是Python解释器在构建列表时用的临时名字,我也很确定你不能直接引用绑定方法;我不清楚有没有这样的明确规则,但那些方法(至少我看过的几个)并不是有效的Python语法。我猜其中一个原因是为了防止我们把它们写进代码里。
所以这就是我所谓的推理链,导致我得出结论,或者说猜测,我把自己困住了。不过我觉得在转身走另一条路之前,应该先和社区确认一下这个想法。
4 个回答
据我所知,无法在列表推导式构建的过程中访问它。
正如KennyTM提到的(如果条目的顺序不重要),你可以使用一个set
(集合)来代替。如果你使用的是Python 2.7或3.1及以上版本,你甚至可以使用集合推导式:
{ some_function(s) for s in raw_data }
否则,使用for
循环也不错(虽然在处理大量数据时效率会很差)
l = []
for s in raw_data:
item = somefunction(s)
if item not in l:
l.append(item)
我不明白你为什么要一次性完成这个。你可以先遍历一下最开始的数据,把重复的部分去掉,或者更简单的方法是像KennyTM建议的那样,把它转换成一个set
,然后再进行列表推导。
需要注意的是,即使你能引用“正在构建的列表”,你的方法还是会失败,因为s
本身并不在列表里,实际上是some_function(s)
的结果在。
以前有一种方法可以做到这一点,利用一个不被文档记录的事实,就是在构建列表的时候,它的值会存储在一个名为 _[1].__self__
的局部变量中。不过这个方法在 Python 2.7 版本中就不再有效了(可能更早就不行了,我当时没太注意)。
如果你先设置一个外部的数据结构,你可以在一个列表推导式中实现你想要的功能。因为你之前的伪代码似乎只是用 this_list
来检查每个 s
是否已经在里面,也就是在做一个成员测试,所以我把它改成了一个叫 seen
的 set
,这样可以优化性能(在一个很大的 list
中检查成员关系可能会很慢)。我说的意思是:
raw_data = [c for c in 'abcdaebfc']
seen = set()
def some_function(s):
seen.add(s)
return s
print [ some_function(s) for s in raw_data if s not in seen ]
# ['a', 'b', 'c', 'd', 'e', 'f']
如果你无法访问 some_function
,你可以在你自己的包装函数中调用它,并在返回之前把它的返回值添加到 seen
这个集合里。
虽然这不是一个列表推导式,但我会把整个过程封装在一个函数里,这样更方便重复使用:
def some_function(s):
# do something with or to 's'...
return s
def add_unique(function, data):
result = []
seen = set(result) # init to empty set
for s in data:
if s not in seen:
t = function(s)
result.append(t)
seen.add(t)
return result
print add_unique(some_function, raw_data)
# ['a', 'b', 'c', 'd', 'e', 'f']
无论哪种情况,我觉得奇怪的是,你想要引用的伪代码中正在构建的列表,并不是 raw_data
值的一个子集,而是对每个值调用 some_function
的结果,也就是经过转换的数据,这自然让人好奇 some_function
到底做了什么,才能让它的返回值和现有的 raw_data
项的值匹配。