带有检查空条件的python生成器

2024-05-12 19:40:47 发布

您现在位置: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. 这看起来不优雅。有没有更好的方法来做同样的事情呢?

示例用法:

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

Tags: inselffalsetrueforgetreturnif
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

使用^{}实现非零测试,只需在创建时缓存它:

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

此版本的不可零缓存标志;因此它只告诉您迭代器在开始时是否为非空。如果您需要能够在iterable生命周期的其他点上测试它,那么可以使用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"

相关问题 更多 >