【问题标题】:python now, next, n iterationpython now, next, n 迭代
【发布时间】:2012-06-22 21:10:27
【问题描述】:

编写一个通用函数,可以迭代任何现在返回的可迭代对象,下一个对。

def now_nxt(iterable):
    iterator = iter(iterable)
    nxt = iterator.__next__()
    for x in iterator:
        now = nxt
        nxt = x
        yield (now,nxt) 

for i in now_nxt("hello world"):
    print(i)

('h', 'e')
('e', 'l')
('l', 'l')
('l', 'o')
('o', ' ')
(' ', 'w')
('w', 'o')
('o', 'r')
('r', 'l')
('l', 'd')

我一直在考虑编写一个可以设置每个元组中项目数的函数的最佳方法。

例如,如果是

func("hello",n=3)

结果是:

('h','e','l')
('e','l','l')
('l','l','o')

我是新手,如果我在这里做错了,请指出:

import timeit

def n1(iterable, n=1):
    #now_nxt_deque
    from collections import deque
    deq = deque(maxlen=n)
    for i in iterable:
        deq.append(i)
        if len(deq) == n:
            yield tuple(deq)

def n2(sequence, n=2):
    # now_next
    from itertools import tee
    iterators = tee(iter(sequence), n)
    for i, iterator in enumerate(iterators):
        for j in range(i):
            iterator.__next__()
    return zip(*iterators)

def n3(gen, n=2):
    from itertools import tee, islice
    gens = tee(gen, n)
    gens = list(gens)
    for i, gen in enumerate(gens):
        gens[i] = islice(gens[i], i, None) 
    return zip(*gens)


def prin(func):
    for x in func:
        yield x

string = "Lorem ipsum tellivizzle for sure ghetto, consectetuer adipiscing elit."

print("func 1: %f" %timeit.Timer("prin(n1(string, 5))", "from __main__ import n1, string, prin").timeit(100000))
print("func 2: %f" %timeit.Timer("prin(n2(string, 5))", "from __main__ import n2, string, prin").timeit(100000))
print("func 3: %f" %timeit.Timer("prin(n3(string, 5))", "from __main__ import n3, string, prin").timeit(100000))

结果:

$  py time_this_function.py 
func 1: 0.163129
func 2: 2.383288
func 3: 1.908363

【问题讨论】:

  • 我觉得不错。我可能会尝试通过两个循环来摆脱 len() 检查:一个用前 n-1 个项目初始化双端队列,然后一个循环产生完整的元组。但我也可能认为只用一个循环会更好。
  • 您可能想考虑只问“如何做到这一点”这个问题,然后将您的内容作为答案发布,而不是将其放在问题中。
  • @Lattyware,感谢您的建议!

标签: python function iterator


【解决方案1】:

我的建议是,

from collections import deque

def now_nxt_deque(iterable, n=1):
    deq = deque(maxlen=n)
    for i in iterable:
        deq.append(i)
        if len(deq) == n:
            yield tuple(deq)

for i in now_nxt_deque("hello world", 3):
    print(i)

('h', 'e', 'l')
('e', 'l', 'l')
('l', 'l', 'o')
('l', 'o', ' ')
('o', ' ', 'w')
(' ', 'w', 'o')
('w', 'o', 'r')
('o', 'r', 'l')
('r', 'l', 'd')

【讨论】:

    【解决方案2】:

    这是一个非常简单的方法:

    • 使用 itertools.tee 克隆您的迭代器 n
    • 推进ith 迭代器i
    • izip他们一起
    import itertools
    
    def now_next(sequence, n=2):
        iterators = itertools.tee(iter(sequence), n)
        for i, iterator in enumerate(iterators):
            for j in range(i):
                iterator.next()
        return itertools.izip(*iterators)
    

    【讨论】:

    • 很好的解决方案!只是一个想法:既然您已经开始使用迭代器,那么坚持这种模式并在最后使用 return itertools.izip(*iterators) 可能是有意义的?
    • @jathanism:好点。为了清楚起见,我只是选择了zip。已编辑。
    • @Eric,这种方法的可扩展性如何?克隆迭代器的成本高吗?
    • @ThemanontheClaphamomnibus:不确定。克隆迭代器比克隆集合更好!不确定我的解决方案会比你的解决方案做得更好。
    • 你应该使用next(iterator),而不是iterator.next()
    【解决方案3】:

    我的解决方案:

    def nn(itr, n):
        iterable = iter(itr)
    
        last = tuple(next(iterable, None) for _ in xrange(n))
        yield last
        for _ in xrange(len(itr)):
            last = tuple(chain(last[1:], [next(iterable)]))
            yield last
    

    这是为 Python 2 制作的,如果你想在 Python 3 中使用它,请将 xrange 替换为 range

    next,有一个很棒的default 参数,它将被返回而不是引发StopIteration,你也可以像这样将这个默认参数添加到你的函数中:

    def nn(itr, n, default=None):
        iterable = iter(itr)
    
        last = tuple(next(iterable, default) for _ in xrange(n))
        yield last
        for _ in xrange(len(itr)):
            last = tuple(chain(last[1:], [next(iterable, default)]))
            yield last
    

    我用它玩了更多,例如使用itr.__class__() 作为默认值,但这对于列表和元组来说似乎是错误的,这对于字符串来说才有意义。

    【讨论】:

    • 这绝对是一个紧凑的解决方案。
    • len(itr) 不适用于生成器,这使得它不太通用。
    • 你是对的,所以我试图找到更好的方法,我记得 itertools 文档,我编辑了帖子。
    • OP 想要重叠子序列,而不是 grouper 产生的。
    • 正确,我删除了,我的错。
    【解决方案4】:

    Eric 使用切片的技术的一种变体

    from itertools import tee, islice, izip
    
    def now_next(gen, n=2):
      gens = tee(gen, n)
      gens = list(gens)
      for i, gen in enumerate(gens):
        gens[i] = islice(gens[i], i, None) 
      return izip(*gens)
    
    for x in now_next((1,2,3,4,5,6,7)):
      print x
    

    【讨论】:

    • 这似乎比 Erics 快,我已将该函数添加到问题中的 timeit 列表中。 - 请注意,由于我使用的是 Python 3.X,所以 izip 现在是 zip
    【解决方案5】:

    基于 cravoori 答案的单线:

    from itertools import tee, islice, izip
    
    def now_next(gen, n=2):
        return izip(*(islice(g, i, None) for i, g in enumerate(tee(gen, n))))
    

    【讨论】:

      猜你喜欢
      • 2014-07-19
      • 2012-02-17
      • 1970-01-01
      • 1970-01-01
      • 2015-11-26
      • 2013-05-24
      • 2018-07-20
      • 1970-01-01
      相关资源
      最近更新 更多