[软件设计/软件工程] 精简的 Python 生成器,第二更短:如何检索静默使用的元素

[复制链接]
发表于 2022-5-3 10:25:44
问题
我想使用 zip 来解析(可能)不同长度的两个生成器:
  1. for el1, el2 in zip(gen1, gen2):
  2.     print(el1, el2)
复制代码

但是,如果 gen2 的元素较少,它将“消耗”元素。一个额外的 gen1 元素。

例如,
  1. def my_gen(n:int):
  2.     for i in range(n):
  3.         yield i

  4. gen1 = my_gen(10)
  5. gen2 = my_gen(8)

  6. list(zip(gen1, gen2))  # Last tuple is (7, 7)
  7. print(next(gen1))  # printed value is "9" => 8 is missing

  8. gen1 = my_gen(8)
  9. gen2 = my_gen(10)

  10. list(zip(gen1, gen2))  # Last tuple is (7, 7)
  11. print(next(gen2))  # printed value is "8" => OK
复制代码

显然,缺少一个值(在我之前的示例中为 8 ),因为它在意识到 gen1 没有更多元素之前已被读取(因此产生值 8 )。但是这个价值在宇宙中消失了。不存在这样的“问题”。当 gen2 是“更长”时。

问题:有没有办法检索这个缺失值(即前面示例中的 gen2)?

注意:我目前正在使用 8 以另一种方式实现它,但我真的很想知道如何获得这个值。

回答
一种方法是实现一个允许您缓存最后一个值的生成器:
  1. class cache_last(collections.abc.Iterator):
  2.     """
  3.     Wraps an iterable in an iterator that can retrieve the last value.

  4.     .. attribute:: obj

  5.        A reference to the wrapped iterable. Provided for convenience
  6.        of one-line initializations.
  7.     """
  8.     def __init__(self, iterable):
  9.         self.obj = iterable
  10.         self._iter = iter(iterable)
  11.         self._sentinel = object()

  12.     @property
  13.     def last(self):
  14.         """
  15.         The last object yielded by the wrapped iterator.

  16.         Uninitialized iterators raise a `ValueError`. Exhausted
  17.         iterators raise a `StopIteration`.
  18.         """
  19.         if self.exhausted:
  20.             raise StopIteration
  21.         return self._last

  22.     @property
  23.     def exhausted(self):
  24.         """
  25.         `True` if there are no more elements in the iterator.
  26.         Violates EAFP, but convenient way to check if `last` is valid.
  27.         Raise a `ValueError` if the iterator is not yet started.
  28.         """
  29.         if not hasattr(self, '_last'):
  30.             raise ValueError('Not started!')
  31.         return self._last is self._sentinel

  32.     def __next__(self):
  33.         """
  34.         Retrieve, record, and return the next value of the iteration.
  35.         """
  36.         try:
  37.             self._last = next(self._iter)
  38.         except StopIteration:
  39.             self._last = self._sentinel
  40.             raise
  41.         # An alternative that has fewer lines of code, but checks
  42.         # for the return value one extra time, and loses the underlying
  43.         # StopIteration:
  44.         #self._last = next(self._iter, self._sentinel)
  45.         #if self._last is self._sentinel:
  46.         #    raise StopIteration
  47.         return self._last

  48.     def __iter__(self):
  49.         """
  50.         This object is already an iterator.
  51.         """
  52.         return self
复制代码

要使用此选项,请将输入包装到 zip 中:
  1. gen1 = cache_last(range(10))
  2. gen2 = iter(range(8))
  3. list(zip(gen1, gen2))
  4. print(gen1.last)
  5. print(next(gen1))
复制代码

重要的是让 gen2 成为一个迭代器而不是一个可迭代的,这样你就可以知道哪个已经用完了。如果 gen2 用尽,则无需检查 gen1.last 。

另一种方法是重写 zip 以接受可变的迭代序列而不是单个迭代。这将允许您用包含“peek”的链接版本替换可迭代对象。物品:
  1. def myzip(iterables):
  2.     iterators = [iter(it) for it in iterables]
  3.     while True:
  4.         items = []
  5.         for it in iterators:
  6.             try:
  7.                 items.append(next(it))
  8.             except StopIteration:
  9.                 for i, peeked in enumerate(items):
  10.                     iterables[i] = itertools.chain([peeked], iterators[i])
  11.                 return
  12.             else:
  13.                 yield tuple(items)

  14. gens = [range(10), range(8)]
  15. list(myzip(gens))
  16. print(next(gens[0]))
复制代码

这种方法有很多问题。它不仅会丢失原始的可迭代对象,还会丢失原始对象可能通过将其替换为链对象而具有的任何有用属性。





上一篇:如何为 UIToolbar 上的按钮添加滚动条?
下一篇:asp.net MVC中如何实现动态资源权限?

使用道具 举报

Archiver|手机版|小黑屋|吾爱开源 |网站地图

Copyright 2011 - 2012 Lnqq.NET.All Rights Reserved( ICP备案粤ICP备14042591号-1粤ICP14042591号 )

关于本站 - 版权申明 - 侵删联系 - Ln Studio! - 广告联系

本站资源来自互联网,仅供用户测试使用,相关版权归原作者所有

快速回复 返回顶部 返回列表