【问题标题】:Join python iterators on key在 key 上加入 python 迭代器
【发布时间】:2015-09-12 07:20:50
【问题描述】:

我正在寻找一种方法来加入像 itertools.izip_longest() 这样的 python 迭代器,但我想加入具有相同“键”(由参数定义)的元素并在键时输出 None并非在所有迭代器上都存在。我假设迭代器按“键”升序排序。

例子:

iter1 = iter((1, 3, 4, 9))
iter2 = iter((3, 5, 6))
iter3 = iter((1, 3, 10))

zipjoiner(iter1, iter2, iter3)

应该给:

iter(((1, None, 1), (3, 3, 3), (4, None, None), (None, 5, None), (None, 6, None), (9, None, None), (None, None, 10)))

(在这种情况下,密钥是默认身份lambda x: x

我尝试修改 python documentation 中的izip_longest() 实现并且它有效(至少在我的示例中),但我正在寻找更优雅的解决方案。 有什么想法吗?

这是我的代码:

def zipjoiner(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    key = kwds.get('key', lambda x: x)
    counter = [len(args) - 1]
    def sentinel():
        if not counter[0]:
            raise ZipExhausted
        counter[0] -= 1
        yield fillvalue
    fillers = itertools.repeat(fillvalue)
    iterators = [itertools.chain(it, sentinel(), fillers) for it in args]

    def getkey(x):
        return None if x is None else key(x)

    try:
        while iterators:
            elements = tuple(map(next, iterators))
            keys = tuple(map(getkey, elements))
            minkey = min(_ for _ in keys if not _ is None)
            while not all(k == minkey for k in keys):
                yield tuple(map(lambda (k, v): v if k == minkey else None, zip(keys, elements)))
                elements = tuple(map(lambda (k, it, v): it.next() if k == minkey else v, zip(keys, iterators, elements)))
                keys = tuple(map(getkey, elements))
                minkey = min(_ for _ in keys if not _ is None)
            yield elements

    except ZipExhausted:
        pass

【问题讨论】:

    标签: python iterator


    【解决方案1】:

    如果您不想保留顺序,您可以将列表转换为集合,然后遍历来自所有输入迭代器的所有值的排序列表,并根据值是否在对应集:

    def join_iterators(*iterators):
        sets = []
        for iterator in iterators:
            sets.append(set(iterator))
    
        values = set(itertools.chain(*iterators))
        get_value_or_none = lambda value, s: value if value in s else None
        for value in sorted(values):
            yield tuple(get_value_or_none(value, s) for s in sets)
    

    这并没有解决您的关键功能,但我认为您知道如何应用它;)

    【讨论】:

      【解决方案2】:

      虽然您可以制作更具可读性的内容,但我认为您无法制作出更高效的内容。

      从可读性的角度来看,我会更改标题和前几行:

      def zipjoiner(*iters, fillvalue=None, key=lambda x: x):
          # drop first two lines dealing with fillvalue and key
      

      因为没有理由与**kwds打交道。

      【讨论】:

        猜你喜欢
        • 2018-11-15
        • 1970-01-01
        • 2012-03-01
        • 1970-01-01
        • 2014-07-19
        • 2012-02-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多