【问题标题】:How to build a generator function to accomplish skipping duplicates如何构建生成器函数来完成跳过重复项
【发布时间】:2018-07-16 00:16:11
【问题描述】:

我想创建一个循环输入可迭代序列的生成器函数,一次生成一个元素,但跳过重复项。示例代码如下:

numbers = [4, 5, 2, 6, 2, 3, 5, 8]
nums = unique(numbers)
    next(nums)
4
    next(nums)
5
    next(nums)
2
    next(nums)
6
    next(nums)
3
    next(nums)
8

有没有人知道为什么这段代码没有打印出来?

def unique(iterable):
    seen = set()
    for n in iterable:
        if n not in seen:
            seen.add(n)
            yield n

numbers = [4, 5, 2, 6, 2, 3, 5, 8]
nums = unique(numbers)
print(next(nums))

【问题讨论】:

  • python中的缩进非常重要,seen.add()yield都需要缩进到if语句之外。
  • 你从哪里得到这个代码?这几乎与itertools 文档中的unique_everseen 配方相同,这让我觉得你是从那里得到的,或者是从熟悉该代码的人那里得到的,并从记忆中重写了它。因此,您应该能够将您的代码与您复制的代码进行比较,并查看缩进的差异。
  • 我这样做了,但它仍然没有改变输出。代码是否还有其他问题可能导致无法打印?
  • @abarnert 我从以下答案之一获得了代码。
  • 如果以下答案之一解决了您的问题,您应该接受它(单击相应答案旁边的复选标记)。这有两件事。它让每个人都知道您的问题已得到您满意的解决,并为帮助您的人提供帮助。有关完整说明,请参阅here

标签: python python-3.x unique


【解决方案1】:

一个简单的唯一生成器将只保留一个set 已看到的项目,例如:

def unique(nums):
    seen = set()
    for n in nums:
        if n not in seen:
            seen.add(n)
            yield n

In []:
numbers = [4, 5, 2, 6, 2, 3, 5, 8]
list(unique(numbers))

Out[]:
[4, 5, 2, 6, 3, 8]

【讨论】:

【解决方案2】:

最简单的方法是use OrderedDict,这是一种在保持顺序的同时进行重复数据删除的简单方法:

from collections import OrderedDict

def unique(nums):
    yield from OrderedDict.fromkeys(nums)

从技术上讲,它运行迅速(所有重复数据删除都是预先完成的,然后您迭代完全重复数据删除的OrderedDict),但所有其他解决方案最终都需要构建一个等效的set,所以这会延迟生产第一个值,但总体上做的工作量相同(在 Python 版本中,使用 C 实现 OrderedDict,运行速度比使用 set 作为“可见”存储的手动生成器更快)。它不适合的情况是无限输入可迭代对象和有限但大的可迭代对象,您可能会在完成之前停止处理它们(在这种情况下,需要基于set 的更懒惰的unique_everseen 解决方案) .

在 Python 3.6 及更高版本上,plain dict preserves order(虽然是 it's not an official guarantee until 3.7),所以您甚至不需要导入:

def unique(nums):
    yield from dict.fromkeys(nums)

【讨论】:

  • 您认为定义有序集(如解决方案here)是否有任何性能优势,或者这会不太效率吗?似乎所有这些解决方法都源于 OrderedSet 的不存在。
  • @jpp:我见过的大多数第三方OrderedSets 都是在 Python 层实现的,所以它们会更慢(尽管内存效率可能略高一些)。 3.6+ 的解决方案(使用普通的dict)实际上比基于plain set 的解决方案更节省内存;新的dict 设计实际上使等效的“仅添加”dict 比等效的set 使用更少的内存,虽然查找和插入速度稍慢,但它们通常在一个数量级内;没有 Python 实现的函数将与 dict 匹配。
  • 知道了,谢谢。补充一下,我在某处读到,Python 开发人员编写了一个或多或少准备好的 C 级 OrderedSet,但用例被认为不足以扩展 collections。可能是因为您概述的解决方案不是太多工作。
【解决方案3】:

您需要发电机有什么原因吗?为什么不直接使用set

numbers = [4, 5, 2, 6, 2, 3, 5, 8]
for i in set(numbers):
    print(i)

如果你真的需要生成器:

def skipper(l):
    for i in set(l):
        yield i

for i in skipper(numbers):
    print(i)

【讨论】:

  • 这并不能保证您获得的数字与原始列表的顺序相同,这可能对 OP 很重要。
  • 另外,旁注:for i in set(l): yield i 可以在现代 Python(自 3.3 起)上简化为 yield from set(l);不能解决排序问题,但使用 yield from 比手动循环 + yield 更快/更简单。
  • @AChampion 想过,但由于从 OP 的问题中省略,没有考虑到它。必须提供所需的条件!
猜你喜欢
  • 1970-01-01
  • 2023-01-16
  • 1970-01-01
  • 1970-01-01
  • 2016-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-21
相关资源
最近更新 更多