【问题标题】:Standard way to convert iterator-returning function into "proper iterable"-returning function?将迭代器返回函数转换为“适当的可迭代”返回函数的标准方法?
【发布时间】:2016-08-17 02:04:17
【问题描述】:

为避免混淆,让我定义

适当的可迭代对象:不是迭代器的可迭代对象。


问: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 课程对我来说看起来很笨拙。我确信从标准库中实现这样的东西会更好。


@尼基塔

试试这个:

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 的东西?

【问题讨论】:

  • 您似乎很困惑,python 中的所有迭代器都是可迭代的。 __iter__ 方法是一个恒等函数这一事实并不重要。您是否要创建一个可多次迭代的可迭代对象?
  • 您是否正在尝试解决某个特定问题?
  • 您的“可迭代制造者”只会一遍又一遍地调用原始迭代器。或者,也许您可​​以使用itertools.tee

标签: python iterator decorator iterable


【解决方案1】:

这没有意义——“迭代器返回函数”变成“可迭代返回函数”。如果一个函数正在返回一个迭代器,那么它已经返回了一个迭代器,因为迭代器是迭代器,因为它们需要有__iter__ 方法。

来自the docs

可迭代

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

迭代器

表示数据流的对象。反复调用 迭代器的__next__() 方法(或将其传递给内置函数 next()) 返回流中的连续项目。当没有更多数据时 而是引发了一个 StopIteration 异常。在此刻, 迭代器对象已用尽并且对其的任何进一步调用 __next__() 方法只是再次引发 StopIteration。

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

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

UPD

我的意思是……

(按步骤显示以进行比较)

案例 1

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

案例 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
  4. next(j),返回一些取决于 some_var 的值。

如您所见,除了准备步骤的额外复杂性之外,没有什么真正改变。

也许您可以提供有关您通过这种“包装”尝试实现的更多信息,以了解真正的问题。

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

UPD2

所以,现在我明白了,目标是将单遍迭代器转换为多遍迭代器......

我的回答:

“标准库是否已经提供了类似于 to_iterable_maker?”

是“否”。但最接近的是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)]

【讨论】:

  • 根据 DSM 的建议,我已将我的问题更新为将原始脚注“升级”到帖子正文。请阅读此更新。另外,请参阅我提供的代码。即使您反对我的问题的措辞,我提供的代码也是明确的。问题归结为:标准库是否已经有一个函数可以做to_iterable_maker 所做的事情,或者以其他方式支持这个功能?
  • @kjo,正如我所见,您正在尝试包装和迭代器以使其可迭代。请查看答案更新。
  • @kjo,不是更清楚您要实现的目标 - 基本上,您想将单遍迭代器转换为多遍迭代器。我提议itertools.tee() 接近了这一点。查看答案更新。注意:我的整个答案都是为你准备的,尽管我希望其他人也能从中受益。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-02
  • 1970-01-01
  • 2017-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多