【问题标题】:Get the nth item of a generator in Python在 Python 中获取生成器的第 n 项
【发布时间】:2017-11-20 19:07:40
【问题描述】:

有没有更简洁的语法写法?

gen = (i for i in xrange(10))
index = 5
for i, v in enumerate(gen):
    if i is index:
        return v

生成器应该有一个gen[index] 表达式似乎很自然,它充当一个列表,但在功能上与上述代码相同。

【问题讨论】:

  • 在这种情况下(或在许多情况下)你不想要isis 用于比较身份,而不是平等。你想要==。这可能会在这种情况下起作用,但只是巧合和实现细节。
  • 我用的是整数,怎么会不行呢?在这种情况下,期望index 对象实现__eq__ 甚至是一种好习惯吗? (这离题了……)
  • 试试1000 is 500 + 500,它(可能)是False。例如,参见stackoverflow.com/questions/306313/…
  • 这个问题+1。说“gen 的第 n 个结果”没有更冗长的方式,这似乎很奇怪。
  • 另一种可能性是拉链——它们处理任意树,但列表也是一棵树。请参阅此实现github.com/trivio/zipper/blob/master/tests/test_zipper.py

标签: python generator


【解决方案1】:

如果n 在创作时已知,则可以使用解构。例如获得第三项:

>>> [_, _, third, *rest] = range(10)
>>> third
2
>>> rest
[3, 4, 5, 6, 7, 8, 9]

【讨论】:

    【解决方案2】:

    一种方法是使用itertools.islice

    >>> gen = (x for x in range(10))
    >>> index = 5
    >>> next(itertools.islice(gen, index, None))
    5
    

    【讨论】:

      【解决方案3】:

      我认为最好的方法是:

      next(x for i,x in enumerate(it) if i==n)
      

      (其中it 是您的迭代器,n 是索引)

      它不需要您添加导入(如使用itertools 的解决方案),也不需要一次将迭代器的所有元素加载到内存中(如使用list 的解决方案)。

      注意 1:如果您的迭代器少于 n 个项目,此版本将引发 StopIteration 错误。如果你想得到None,你可以使用:

      next((x for i,x in enumerate(it) if i==n), None)
      

      注意 2:next 的调用中没有括号。这不是列表推导,而是生成器推导,它不会消耗原始迭代器的第 n 个元素。

      【讨论】:

      • 我相信这将遍历整个迭代器,当迭代器需要很长时间才能完成时,它会变慢。
      • @ubershmekel:不,不会!它将遍历前 n 个元素(当然),仅此而已。你为什么不自己试试呢?
      • 我添加了第二个注释,表明原始迭代器没有完全消耗
      【解决方案4】:

      最好使用的是: 示例:

      a = gen values ('a','c','d','e')
      

      所以答案是:

      a = list(a) -> this will convert the generator to a list (it will store in memory)
      

      那么当你想去特定的索引时,你会:

      a[INDEX] -> and you will able to get the value its holds 
      

      如果您只想知道计数或执行不需要存储在内存中的操作,最佳做法是: a = sum(1 in i in a) -> 这将计算您拥有的对象的数量

      希望我让它更简单。

      【讨论】:

        【解决方案5】:

        我反对将生成器视为列表的诱惑。简单但幼稚的方法是简单的单行:

        gen = (i for i in range(10))
        list(gen)[3]
        

        但请记住,生成器不像列表。他们不会将中间结果存储在任何地方,因此您不能倒退。我将在 python repl 中用一个简单的例子来演示这个问题:

        >>> gen = (i for i in range(10))
        >>> list(gen)[3]
        3
        >>> list(gen)[3]
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        IndexError: list index out of range
        

        一旦你开始通过生成器获取序列中的第 n 个值,生成器现在处于不同的状态,再次尝试获取第 n 个值将返回不同的结果,这可能会导致代码中的错误。

        让我们看看另一个例子,基于问题中的代码。

        人们最初会期望以下内容会打印两次4

        gen = (i for i in range(10))
        index = 4
        for i, v in enumerate(gen):
            if i == index:
                answer = v
                break
        print(answer)
        for i, v in enumerate(gen):
            if i == index:
                answer = v
                break
        print(answer)
        

        但是在repl中输入这个,你会得到:

        >>> gen = (i for i in range(10))
        >>> index = 4
        >>> for i, v in enumerate(gen):
        ...     if i == index:
        ...             answer = v
        ...             break
        ... 
        >>> print(answer)
        4
        >>> for i, v in enumerate(gen):
        ...     if i == index:
        ...             answer = v
        ...             break
        ... 
        >>> print(answer)
        9
        

        祝你好运追踪那个错误。

        编辑:

        正如所指出的,如果生成器无限长,您甚至无法将其转换为列表。表达式list(gen) 永远不会结束。

        有一种方法可以在无限生成器周围放置一个延迟评估的缓存包装器,使其看起来像一个无限长的列表,您可以随意索引,但这值得它自己的问题和答案,并且会对性能产生重大影响.

        【讨论】:

        • 如果生成器是无限的怎么办?
        • 这应该更高,因为这样做会花费大量时间。感谢您指出这一点。
        【解决方案6】:

        我首先想到的是:

        gen = (i for i in xrange(10))
        index = 5
        
        for i, v in zip(range(index), gen): pass
        
        return v
        

        【讨论】:

          【解决方案7】:

          您可以这样做,使用count 作为示例生成器:

          from itertools import islice, count
          next(islice(count(), n, n+1))
          

          【讨论】:

          • 这是什么版本的 Python?上面的代码在 3.3 中给了我错误AttributeError: 'itertools.islice' object has no attribute 'next'
          • 在 Python 3x 中,将next 更改为__next__(),即islice(count, n, n=1).__next__()
          • 所以最好使用next(islice(count(), n, n+1))
          • 我认为你可以摆脱上限,即next(islice(count(), n, None))
          【解决方案8】:

          也许您应该详细说明一个实际用例。

          >>> gen = xrange(10)
          >>> ind=5 
          >>> gen[ind]
          5
          

          【讨论】:

          • 我将xrange(10) 编辑为(i for i in xrange(10))。原来这种语法适用于xrange,因为它不是真正的生成器......
          • xrange 早于生成器,并返回一个 xrange 对象,它实际上实现了完整的序列协议。
          猜你喜欢
          • 2015-08-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多