列表推导:对组件的引用

8 投票
4 回答
2427 浏览
提问于 2025-04-16 12:09

总的来说:我需要写一个列表推导式,但在这个推导式中我想引用正在创建的列表。

这可能不是你每天都需要做的事情,但我觉得也不算特别奇怪。

也许这里没有答案——不过请不要告诉我应该用for循环。虽然这样可能是对的,但对我没有帮助。原因是这个代码片段是一个ETL模块的一部分,所以性能很重要,同时我也想避免创建一个临时容器——这就是我想用列表推导式来写这一步的原因。如果用for循环能解决我的问题,我早就写一个了。

无论如何,我就是写不出这个特定的列表推导式。原因是:我需要写的表达式是这个样子的:

[ some_function(s) for s in raw_data if s not in this_list ]

在这个伪代码中,“this_list”指的是通过评估这个列表推导式而创建的列表。这就是我卡住的原因——因为这个列表在我的列表推导式被评估之前是不存在的,所以我不知道该怎么引用它。

到目前为止,我考虑过的事情(可能基于一个或多个错误的假设,虽然我不太确定具体在哪里):

  • 难道Python解释器不需要给这个正在构建的列表起个名字吗?我觉得是的。

  • 这个临时名字可能是从某个用于构建我列表的绑定方法中来的(比如'sum'?)

  • 但即使我费心去找那个绑定方法,假设它确实是Python解释器在构建列表时用的临时名字,我也很确定你不能直接引用绑定方法;我不清楚有没有这样的明确规则,但那些方法(至少我看过的几个)并不是有效的Python语法。我猜其中一个原因是为了防止我们把它们写进代码里。

所以这就是我所谓的推理链,导致我得出结论,或者说猜测,我把自己困住了。不过我觉得在转身走另一条路之前,应该先和社区确认一下这个想法。

4 个回答

2

据我所知,无法在列表推导式构建的过程中访问它。

正如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)
3

我不明白你为什么要一次性完成这个。你可以先遍历一下最开始的数据,把重复的部分去掉,或者更简单的方法是像KennyTM建议的那样,把它转换成一个set,然后再进行列表推导。

需要注意的是,即使你能引用“正在构建的列表”,你的方法还是会失败,因为s本身并不在列表里,实际上是some_function(s)的结果在。

3

以前有一种方法可以做到这一点,利用一个不被文档记录的事实,就是在构建列表的时候,它的值会存储在一个名为 _[1].__self__ 的局部变量中。不过这个方法在 Python 2.7 版本中就不再有效了(可能更早就不行了,我当时没太注意)。

如果你先设置一个外部的数据结构,你可以在一个列表推导式中实现你想要的功能。因为你之前的伪代码似乎只是用 this_list 来检查每个 s 是否已经在里面,也就是在做一个成员测试,所以我把它改成了一个叫 seenset,这样可以优化性能(在一个很大的 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 项的值匹配。

撰写回答