如何在理解中使用哨兵列表?

how to use a sentinel list in a comprehension?

我有一个列表

In [4]: a = [1, 2, 3, 3, 2, 4]

我想使用哨兵列表通过理解从中删除重复项(原因见下文):

In [8]: [x if x not in seen else seen.append(x) for x in a]
Out[8]: [1, 2, 3, 3, 2, 4]

seen好像没有考虑(既没有更新,也没有检查)。为什么会这样?

至于为什么用绕口令的原因:我手上的表是这样的

[{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]

并且我想根据特定键的值删除重复项(b 在上面的例子中,留下 [{'a': 3, 'b': 4}, {'a': 5, 'b': 5}] (我不关心删除了哪个字典)。这个想法将构建一个值为 b 的哨兵列表,并仅保留没有 b 的字典等于该哨兵列表中的任何元素。

由于 x 不在 seen 中,您也永远不会将其添加到 seen 中; else 分支在 x not in seen 为真时不执行。

但是,您使用的是条件表达式;它总是产生一个值; xseen.append() 的结果(即 None),所以你没有过滤,你在这里 映射

如果你想过滤,将测试移动到if部分 for循环之后:

seen = set()
[x for x in a if not (x in seen or seen.add(x))]

因为您使用的是 seen.append(),所以我认为您使用的是列表;我把你换成了 set(),因为使用集合测试成员资格测试要快得多。

所以 x 排除 仅当 a) x in seen 为真(所以我们已经看到了),或者 seen.append(x) 返回一个真值(None 不是真的)。是的,这行得通,只是有点复杂。

演示:

>>> a = [1, 2, 3, 3, 2, 4]
>>> seen = set()
>>> [x for x in a if not (x in seen or seen.add(x))]
[1, 2, 3, 4]
>>> seen
set([1, 2, 3, 4])

将此应用于您的具体问题:

>>> a = [{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]
>>> seen = set()
>>> [entry for entry in a if not (entry['b'] in seen or seen.add(entry['b']))]
[{'a': 3, 'b': 4}, {'a': 5, 'b': 5}]

你永远不会执行 if 的 else 部分,因为你不会在第一次匹配时更新。你可以这样做:

 [seen.append(x) or x for x in lst if x not in seen]

这样 or return 是最后一个值(并使用 append 执行更新(总是 return 是 None,让the 或继续寻找 truth-y 值)。

也许您可以利用 dict 键是为此设置的这一事实。如果您想对最后一项进行优先排序,请使用 reversed(最后一项在此处具有优先级):

>>> lst = [{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]
>>> filtered = {item['b']: item for item in reversed(lst)}
>>> filtered.values()
[{'a': 3, 'b': 4}, {'a': 5, 'b': 5}]

这使用 'b' 作为将值映射到的键,因此只能将单个元素映射到值 'b',这有效地创建了一个集合 'b'.

注意: 这将 return 值随机排序。为了很好地修复它,对于大数据集,我会创建另一个映射,每个对象到它在原始列表中的索引 (O(n)),并将该映射用作最终结果的排序函数 (O(n*登录(n)))。这超出了这个答案的范围。

我总是对使用运算符优先级作为执行流程控制感到不安。我觉得下面的内容稍微更加明确和可口,尽管它确实带来了元组创建的额外成本。

b_values = set()
[(item, b_values.add(item['b']))[0] for item in original_list
                                    if item['b'] not in b_values]

但实际上当你处于 maintaining/updating 某种状态时,我认为最好的格式是简单的 for 循环:

output_list = []
b_values = set()
for item in original_list:
    if item['b'] not in b_values:
        output_list.append(item)
        b_values.add(item['b'])