【问题标题】:python generator with check for empty condition带有检查空条件的python生成器
【发布时间】:2012-07-13 02:58:21
【问题描述】:

在大多数情况下,python 生成器是列表的良好替代品,我希望在其中检查空条件,而这对于普通生成器是不可能的。我正在尝试编写一个包装器,它允许检查空条件,但仍然很懒惰并提供生成器的好处。

class mygen:
  def __init__(self,iterable):
    self.iterable = (x for x in iterable)
    self.peeked = False
    self.peek = None
  def __iter__(self):
    if self.peeked:
      yield self.peek
      self.peeked = False
    for val in self.iterable:
      if self.peeked:
        yield self.peek
        self.peeked = False
      yield val
    if self.peeked:
      yield self.peek
      self.peeked = False
  def __nonzero__(self):
    if self.peeked:
      return True
    try:
      self.peek = self.iterable.next()
      self.peeked = True
      return True
    except:
      return False
  1. 我认为它的行为与普通生成器一样正确。是否有任何角落案例 我迷路了?
  2. 这看起来不太优雅。有没有更好的 Pythonic 方式来做同样的事情?

示例用法:

def get_odd(l):
    return mygen(x for x in l if x%2)

def print_odd(odd_nums):
  if odd_nums:
      print "odd numbers found",list(odd_nums)
  else:
      print "No odd numbers found"

print_odd(get_odd([2,4,6,8]))
print_odd(get_odd([2,4,6,8,7]))

【问题讨论】:

  • (x for x in iterable) 是一种缓慢而冗长的写法iter(iterable)
  • 你想让__nonzero__()返回当前是否有剩余的项目,或者iterable是否从一开始就为空?
  • 我要看看还有没有剩余的东西。

标签: python generator


【解决方案1】:

使用itertools.tee 实现非零测试,并在创建时简单地对其进行缓存:

from itertools import tee

class NonZeroIterable(object):
    def __init__(self, iterable):
        self.__iterable, test = tee(iter(iterable))
        try:
            test.next()
            self.__nonzero = True
        except StopIteration:
            self.__nonzero = False                 

    def __nonzero__(self):
        return self.__nonzero

    def __iter__(self):
        return self.__iterable

小演示:

>>> nz = NonZeroIterable('foobar')
>>> if nz: print list(nz)
... 
['f', 'o', 'o', 'b', 'a', 'r']
>>> nz2 = NonZeroIterable([])
>>> if not nz2: print 'empty'
... 
empty

这个版本的 NonZeroIterable 缓存了标志;因此它 only 告诉您迭代器是否在开始时为非空。如果您需要能够在其生命周期的其他点测试迭代,请改用Sven's version__nonzero__ 标志会在每次迭代后告诉您是否还有更多项目。

关于您的示例的附注

您的示例代码太简单了,不适合您的用例;您首先测试非空性(可能会遍历输入列表以搜索奇数),但无论如何都会耗尽整个迭代器。以下代码同样高效,并且不需要您发明打破 python 习语的方法:

def print_odd(odd_nums):
    odd_nums = list(odd_nums)
    if odd_nums:
        print "odd numbers found", odd_nums
    else:
        print "No odd numbers found"

【讨论】:

  • 这与原始代码不同。原代码中__nonzero__()返回迭代器是否耗尽。
  • @SvenMarnach:但他从不以那种方式使用它。更新了一个变体,它会在用尽时切换。
  • 据我了解,OP 的想法是__nonzero__ 应该测试底层生成器是否至少产生一次,无论它当前是否已用尽。所以,第一个 sn-p 是正确的。
  • @SvenMarnach:看,这就是为什么这整件事一开始就是个坏主意。此外,我们正在将__nonzero__ 的概念延伸到任何情况下的临界点。
  • @thg435:嗯,这不是原始代码的作用,帖子也不是很清楚。让我们等待 OP 说什么。
【解决方案2】:

我通常不会实现这种 的发电机。有一种惯用的方法可以测试迭代器 it 筋疲力尽:

try:
    next_item = next(it)
except StopIteration:
    # exhausted, handle this case

用一些特定于项目的 LBYL 成语替换这个 EAFP 成语似乎 令人困惑,根本没有好处。

也就是说,如果我真的想这样做,我会这样做:

class MyIterator(object):
    def __init__(self, iterable):
        self._iterable = iter(iterable)
        self._exhausted = False
        self._cache_next_item()
    def _cache_next_item(self):
        try:
            self._next_item = next(self._iterable)
        except StopIteration:
            self._exhausted = True
    def __iter__(self):
        return self
    def next(self):
        if self._exhausted:
            raise StopIteration
        next_item = self._next_item
        self._cache_next_item()
        return next_item
    def __nonzero__(self):
        return not self._exhausted

【讨论】:

  • 我确实看到了检查空虚的意义;如果您想要么 循环遍历迭代器的元素, 在没有元素时做一些特殊的事情,这会非常方便。不过,对于简单的前瞻迭代器 +1。
  • @larsmans:我确实也看到了检查空迭代器的意义,所以我会使用惯用的方式来做这件事。 :)
  • 我只想提一下,__nonzero__() 在 Python3 中变成了 __bool__(),以防有人读到这篇文章
猜你喜欢
  • 2017-03-30
  • 2021-07-25
  • 2019-12-13
  • 2018-11-08
  • 2015-05-20
  • 1970-01-01
  • 1970-01-01
  • 2019-10-05
  • 1970-01-01
相关资源
最近更新 更多