问题 检查空状态的python生成器


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]))

2139
2017-07-13 09:09


起源

(x for x in iterable) 写作是一种缓慢而冗长的写作方式 iter(iterable)。 - Sven Marnach
你要吗 __nonzero__() 返回当前是否有项目,或者迭代是否从一开始就是空的? - Sven Marnach
我想要是否还剩下任何物品。 - balki
有关: stackoverflow.com/q/661603/281545 - Mr_and_Mrs_D


答案:


我通常不会实现这种方式 发电机。如何测试迭代器有一种惯用的方法 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

8
2017-07-13 09:25



我确实看到了检查空虚的重点;如果你愿意,这可以非常方便 或 遍历迭代器的元素, 要么 没有时做一些特别的事。仍然,+1为简单的超前迭代器。 - Fred Foo
@larsmans:我确实看到了检查空迭代器的重点,所以我会用惯用的方法来做这个。 :) - Sven Marnach
我只是想提一下__nonzero __()在Python3中成为__bool __(),万一有人读过这个 - madtyn


使用 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缓存了标志;因此 只要 告诉你迭代器在开始时是否为非空。如果您需要能够在其生命周期的其他点测试迭代,请使用 斯文的版本 代替;那里 __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"

3
2017-07-13 09:26



这与原始代码有所不同。在原始代码中, __nonzero__() 返回迭代器是否耗尽。 - Sven Marnach
@SvenMarnach:但他从不以那种方式使用它。更新了一个在耗尽时切换它的变体。 - Martijn Pieters♦
根据我的理解,OP的想法就是这样 __nonzero__ 应该测试基础发电机是否至少产生一次,无论它是否已经耗尽。所以,第一个片段是正确的。 - georg
@SvenMarnach:看,这就是为什么这一切都是一个坏主意的原因..而且,我们正在伸展的概念 __nonzero__ 在任何情况下都要突破。 - Martijn Pieters♦
@ thg435:嗯,这不是原始代码的作用,帖子不是很清楚。让我们等待OP说的话。 - Sven Marnach