【问题标题】:How can I generate unique itertools chains?如何生成唯一的 itertools 链?
【发布时间】:2017-03-08 04:25:13
【问题描述】:

例如,itertools.chain() 相当于:

set.union({1,2,3},{3,4,2,5},{1,6,2,7})

(显然返回的是一个生成器,而不是一个集合)

【问题讨论】:

  • 你能提供一些输入和输出吗?从所使用的术语中不清楚你在问什么。
  • 我不认为你可以用一个可迭代的方法来做到这一点:需要某种方法来查看当前元素是否已经存在于输出中,因此需要存储输出并附加到它一路走来。

标签: python python-3.x unique itertools


【解决方案1】:

itertools 中没有任何内容可以直接为您执行此操作。

为了避免产生重复的项目,您需要跟踪已经产生的项目,这样做的明显方法是使用集合。这是itertools.chain() 的简单包装器:

from itertools import chain

def uniq_chain(*args, **kwargs):
    seen = set()
    for x in chain(*args, **kwargs):
        if x in seen:
            continue
        seen.add(x)
        yield x

...它正在行动:

>>> list(uniq_chain(range(0, 20, 5), range(0, 20, 3), range(0, 20, 2)))
[0, 5, 10, 15, 3, 6, 9, 12, 18, 2, 4, 8, 14, 16]

或者,如果您更喜欢由较小的构建块组成解决方案(这是一种更灵活和“itertoolsy”的方法),您可以编写一个通用的uniq() 函数并将其与chain() 结合起来:

def uniq(iterable):
    seen = set()
    for x in iterable:
        if x in seen:
            continue
        seen.add(x)
        yield x

在行动:

>>> list(uniq(chain(range(0, 20, 5), range(0, 20, 3), range(0, 20, 2))))
[0, 5, 10, 15, 3, 6, 9, 12, 18, 2, 4, 8, 14, 16]

【讨论】:

  • 感谢@Zero,那么我还需要做什么才能获取已删除的实例?
【解决方案2】:

有 3 种方法可以做到这一点:

  1. 您可以按照 Python itertools recipe 文档中的建议使用 more-itertoolshere 中的 unique_everseen

  2. 此外,如果您向下滚动 itertools 配方,您将看到 Python 的 unique_everseen 配方:

    def unique_everseen(iterable, key=None):
         "List unique elements, preserving order. Remember all elements ever seen."
        # unique_everseen('AAAABBBCCDAABBB') --> A B C D
        # unique_everseen('ABBCcAD', str.lower) --> A B C D
        seen = set()
        seen_add = seen.add
        if key is None:
            for element in filterfalse(seen.__contains__, iterable):
                seen_add(element)
                yield element
        else:
            for element in iterable:
                k = key(element)
                if k not in seen:
                    seen_add(k)
                    yield element
    
  3. 有趣的是,你也可以在importlib_metadata._itertools.unique_everseen找到这个功能。

    >>> from importlib_metadata._itertools import unique_everseen
    >>> list(unique_everseen('AAAABBBCCDAABBB'))
    ['A', 'B', 'C', 'D']
    

    但是,我认为它不是供我们使用的(因为他们像那样对我们隐藏了这个功能)。

【讨论】:

    【解决方案3】:

    你可以这样做:

    def chain_unique(*args):
        seen = set()
        yield from (v for v in chain(*args) if v not in seen and not seen.add(v))
    

    【讨论】:

    • 它可以,但我认为将这么多逻辑打包到一行代码中并不是pythonic
    猜你喜欢
    • 1970-01-01
    • 2018-10-20
    • 1970-01-01
    • 2018-05-04
    • 2018-02-28
    • 2013-03-31
    • 2017-12-06
    • 1970-01-01
    相关资源
    最近更新 更多