将迭代器返回函数转换为“适当的可迭代” - 返回函数的标准方法?

时间:2016-04-23 12:24:26

标签: python iterator decorator iterable

为避免混淆,让我定义

  

正确的可迭代:一个不是迭代器的可迭代对象。

问: Python的标准库是否已经提供了将“迭代器返回函数”转换为“正确的可迭代 - 返回函数”的方法?

我以为我在某个地方见过这个,但现在我找不到了。特别是,我浏览了itertools的文档,但没有发现它。

FWIW,这个本土的实现似乎有效:

def to_iterable_maker(iterator_maker):

    def iterable_maker(*args, **kwargs):
        class nonce_iterable(object):
            def __iter__(self):
                return iterator_maker(*args, **kwargs)
        return nonce_iterable()

    return iterable_maker

...但那里的一次性nonce_iterable课对我来说看起来很笨拙。我确信从标准库中实现这样的事情会好得多。

@Nikita

试试这个:

import itertools

base = range(3)
poops_out = itertools.permutations(base)

print list(poops_out)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]

print list(poops_out)
# []

myperms = to_iterable_maker(itertools.permutations)
keeps_going = myperms(base)

print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]

print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]

print list(keeps_going)
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]

itertools.permutationsto_iterable_maker(itertools.permutations)返回的值之间的差异。我的问题是:标准库是否已提供与to_iterable_maker类似的内容?

1 个答案:

答案 0 :(得分:0)

它没有意义 - "迭代器返回功能"进入一个可迭代的返回函数"。如果一个函数返回一个迭代器,那么它已经返回一个iterable,因为迭代器是迭代的,因为它们需要__iter__方法。

来自the docs

  

<强>迭代

     

一个能够一次返回一个成员的对象。示例   iterables包括所有序列类型(例如list,str和tuple)   和一些非序列类型,如dict,文件对象和对象   您使用__iter__()或__getitem__()方法定义的任何类。   Iterables可用于for循环和许多其他地方   需要序列(zip(),map(),...)。当可迭代对象是   作为参数传递给内置函数iter(),它返回一个   对象的迭代器。这个迭代器很适合一次通过   一组价值观。使用iterables时,通常没有必要   调用iter()或自己处理迭代器对象。 for语句   为您自动执行此操作,创建临时的未命名变量   在循环期间保持迭代器。另见迭代器,   序列和生成器。

     

<强>迭代

     

表示数据流的对象。反复拨打电话   迭代器的__next__()方法(或将其传递给内置函数   next())返回流中的连续项。什么时候没有数据   可用的是引发StopIteration异常。在此刻,   迭代器对象已耗尽,并且对其进行任何进一步调用   __next__()方法再次提升StopIteration。

     

迭代器必须有一个__iter__()方法,它返回迭代器对象本身,因此每个迭代器也是可迭代的

     

并且可以在大多数接受其他迭代的地方使用。一   值得注意的例外是尝试多次迭代传递的代码。一个   容器对象(例如列表)每个都会生成一个全新的迭代器   你把它传递给iter()函数或在for循环中使用它的时间。   尝试使用迭代器只会返回相同的耗尽   在上一次迭代过程中使用的迭代器对象,使其出现   就像一个空容器。

UPD

我的意思是......

(按步骤显示比较)

案例1

  1. f = to_iterable_maker(iterator_maker);
  2. i = f(some_var)i nonce_iterable __iter__;
  3. j = iter(i)jiterator_maker(some_var)返回的迭代器;
  4. next(j),返回一些取决于some_var的值。
  5. 案例2

    1. f = iterator_maker;
    2. i = f(some_var)i是等于iterator_maker(some_var)的迭代器,其__iter__(根据迭代器协议);
    3. j = iter(i)jiterator_maker(some_var)返回的迭代器,因为在迭代器上调用__iter__会返回自身,因此j is i会返回true; < / LI>
    4. next(j),返回一些取决于some_var的值。
    5. 正如您所看到的,除了准备步骤中的其他复杂情况外,没有任何改变。

      可能您可以通过此类&#39;包装来提供有关您尝试实现的内容的其他信息,以了解真正的问题。

      根据你的问题,我无法想到任何库函数,它会将迭代器变成可迭代的,因为它已经是。如果您尝试复制迭代器,可以查看itertools.tee()

      <强> UPD2

      所以,现在我看到,目标是将单通道迭代器转换为多通道迭代器...

      我的回答:

        

      &#34;标准库是否已提供类似的内容   to_iterable_maker&#34;

      是&#34;否&#34;。但最接近的是itertools.tee(),它可以帮助您将单个迭代器克隆成几个,然后可以使用。关于你的例子:

      import itertools
      
      base = range(3)
      poops_out = itertools.permutations(base)
      
      iterators = itertools.tee(poops_out, 4)
      
      #You shouldn't use original iterator after clonning, so make it refer to a clone
      #to be used again, otherwise ignore the following line
      poops_out, iterators = iterators[0], iterators[1:]
      
      for it in iterators:
          print list(it)
      
      #Prints:
      #[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      #[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      #[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      

      从迭代器获取迭代的另一种常见方法是使用list()tuple()进行转换,这将允许多次传递:

      import itertools
      
      base = range(3)
      
      poops_out = itertools.permutations(base)
      
      #Obviously poops_out gets consumed at the next line, so it won't iterate anymore
      keeps_going = tuple(poops_out)
      
      print list(poops_out)
      # []
      
      print list(poops_out)
      # []
      
      print list(keeps_going)
      # [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      
      print list(keeps_going)
      # [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      
      print list(keeps_going)
      # [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      

      上述两种方法在内存使用方面可能都很重,因此有时它不是一种选择。在这种情况下,您遇到的解决方案将运作良好。另一个实现,我可以想到,并且更加面向对象,但与你的不同之处在于:

      class IterableMaker(object):
          '''Wraps function returning iterator into "proper" iterable'''
      
          def __init__(self, iterator_maker):
              self.f = iterator_maker
      
          def __call__(self, *args, **kwargs):
              self.args = args
              self.kwargs = kwargs
              return self
      
          def __iter__(self):
              return self.f(*self.args, **self.kwargs)
      

      用法相同:

      import itertools
      
      class IterableMaker(object):
      
          def __init__(self, iterator_maker):
              self.f = iterator_maker
      
          def __call__(self, *args, **kwargs):
              self.args = args
              self.kwargs = kwargs
              return self
      
          def __iter__(self):
              return self.f(*self.args, **self.kwargs)
      
      base = range(3)
      poops_out = itertools.permutations(base)
      
      print list(poops_out)
      # [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      
      print list(poops_out)
      # []
      
      my_perm = IterableMaker(itertools.permutations)
      
      keeps_going = my_perm(base)
      
      print list(keeps_going)
      # [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      
      print list(keeps_going)
      # [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
      
      print list(keeps_going)
      # [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]