【问题标题】:Previous in yield operations - python以前的收益操作 - python
【发布时间】:2021-08-23 18:47:16
【问题描述】:

最近我一直在 python 中使用'yield'。而且我发现生成器函数非常有用。我的问题是,是否有一些东西可以减少生成器对象中的想象光标。只是 next(genfun) 如何移动和输出容器中的第 +i 个项目,我想知道是否存在任何可能调用类似 previous(genfun) 并移动到容器中的第 -1 个项目的函数。

实际工作

def wordbyword():
  words = ["a","b","c","d","e"]
  for word in words:
    yield word

getword = wordbyword()

next(getword)
next(getword)

输出的

a
b

我希望看到和实现的是

def wordbyword():
  words = ["a","b","c","d","e"]
  for word in words:
    yield word

getword = wordbyword()

next(getword)
next(getword)
previous(getword)

预期输出

a
b
a

这可能听起来很傻,但是生成器中是否有这个previous,如果没有,为什么会这样?为什么我们不能减少迭代器,或者我对现有方法一无所知,请稍加说明。什么是实现我手头的最接近的方法。

【问题讨论】:

  • 还有itertools.tee,它允许你多次迭代某些东西。实际上,它只是将中间体存储在幕后的列表中,但它与 Python 中的多通道生成器一样接近。

标签: python list function yield next


【解决方案1】:

一种选择是使用具有自定义__next__ 方法的class 包装wordbyword。这样,您仍然可以使用内置的next 函数来按需使用生成器,但是该类将存储来自next 调用的所有过去结果,并通过previous 属性使其可访问:

class save_last:
   def __init__(self, f_gen):
      self.f_gen = f_gen
      self._previous = []
   def __next__(self):
      self._previous.append(n:=next(self.i_gen))
      return n
   def __call__(self, *args, **kwargs):
      self.i_gen = self.f_gen(*args, **kwargs)
      return self
   @property
   def previous(self):
      if len(self._previous) < 2:
         raise Exception
      return self._previous[-2] 

@save_last
def wordbyword():
   words = ["a","b","c","d","e"]
   for word in words:
      yield word

getword = wordbyword()
print(next(getword))
print(next(getword))
print(getword.previous)

输出:

a
b
a

【讨论】:

    【解决方案2】:

    对生成器对象进行迭代会消耗其元素,因此在使用 next 后没有什么可返回的。您可以将生成器转换为 list 并实现您自己的 nextprevious

    index = 0
    
    
    def next(lst):
        global index
        index += 1
        if index > len(lst):
            raise StopIteration
        return lst[index - 1]
    
    
    def previous(lst):
        global index
        index -= 1
        if index == 0:
            raise StopIteration
        return lst[index - 1]
    
    getword = list(wordbyword())
    print(next(getword)) # a
    print(next(getword)) # b
    print(previous(getword)) # a
    

    【讨论】:

      【解决方案3】:

      如前所述,没有这样的功能,因为生成器的全部意义在于占用较小的内存。您需要存储结果。

      您可以自动存储以前的结果。生成器的一个用例是当您有一个概念上无限的列表(例如素数列表)时,您只需要一个初始段。您可以编写一个生成器来构建这些初始段作为副作用。有一个可选的history 参数,生成器在产生时附加到该参数。例如:

      def wordbyword(history = None):
        words = ["a","b","c","d","e"]
        for word in words:
            if isinstance(history,list): history.append(word)
            yield word
      

      如果您使用不带参数的生成器getword = wordbyword(),它将像普通的生成器一样工作,但如果您传递一个列表,该列表将存储增长历史:

      hist = []
      getword = wordbyword(hist)
      
      print(next(getword)) #a
      print(next(getword)) #b
      print(hist) #['a','b']
      

      【讨论】:

        【解决方案4】:

        没有这样的函数可以在生成器函数中返回。原因是 Python 本身不会在生成器函数中存储先前的值,并且由于它不存储它,它也无法执行重新计算。

        例如,如果您的生成器是时间敏感的函数,例如

        def time_sensitive_generator():
            yield datetime.now()
        

        您将无法重新计算此生成器函数中的先前值。

        当然,这只是无法计算先前值的众多可能情况之一,但就是这样。

        如果你不自己存储价值,它将永远丢失。

        【讨论】:

          猜你喜欢
          • 2019-09-19
          • 1970-01-01
          • 2011-11-13
          • 2016-08-07
          • 2013-07-03
          • 2018-06-26
          • 2019-03-22
          • 2012-08-15
          • 2010-12-08
          相关资源
          最近更新 更多