创建迭代器并在几个迭代源中进行选择

时间:2016-03-26 07:48:09

标签: python python-3.x iterator next redefinition

我想创建一个具有多个"来源的迭代器"要迭代的对象我希望能够为其__next__()方法提供一个可选的关键字参数,该参数可以选择源(没有关键字参数意味着,只需随机选择一个源)。

使用__next__()会导致问题(见下文),因此我已经编写了这个令人不满意的代码:

#!/usr/bin/env python3

import random

class infinite_iterator(object):

    def __init__(self, sources):
        self.collector = []
        for s in sources:
            random.shuffle(s)
            self.collector.append([])
        self.sources = sources

    def __iter__(self):
        return self

    # Workaround: calling it next instead of __next__
    # (not the python3 way...)
    def next(self, **kwargs):
        sce_nb = random.choice([ n for n in range(len(self.sources)) ])
        if 'choice' in kwargs:
            sce_nb = kwargs['choice']
        self.collector[sce_nb].append(self.sources[sce_nb][0])
        output = self.sources[sce_nb].pop(0)
        # Repopulate any empty 'source'
        if not self.sources[sce_nb]:
            (self.sources[sce_nb], self.collector[sce_nb]) = \
                         (self.collector[sce_nb], self.sources[sce_nb])
            random.shuffle(self.sources[sce_nb])
        return output


S = infinite_iterator([["Adam", "Paul", "Oliver", "Aaron", "Joshua", "Jack"],
                       ["Mary", "Sophia", "Emily", "Isobel", "Grace", "Alice", "Lucy"]])

print("Any name: " + S.next())
print("Any girl's name: " + S.next(choice=1))
print("Any boy's name: " + S.next(choice=0))

问题是,如果我想写def __next__(self, **kwargs):以使infinite_iterator成为真正的迭代器,那么当然我想写:

print("Any name: " + next(S))
print("Any girl's name: " + next(S, choice=1))
print("Any boy's name: " + next(S, choice=0))

但得到错误(第2行):

TypeError: next() takes no keyword arguments

我认为此调用next(S, choice=1)将使用对象中定义的__next__()函数。由于这个错误,我认为一方面它实际上没有。这可能是可以预料到的,因为它并不完全是重新定义,因为infinite_iterator不会继承自" Iterator对象" (据我所知,没有这样的对象)。但另一方面,如果我只调用next(S)它可以正常工作,在这种情况下,实际上会调用我的 __next__()方法(它会随机选择一个列表进行迭代)。

最后,是否有实现这种迭代器的解决方案?

1 个答案:

答案 0 :(得分:0)

撰写generator(技术上是协程)并使用send传递您的选择:

import random
def selector(sources):
    option = None
    while True:
        if option is None:
            option = yield random.choice(random.choice(sources))
        else:
            option = yield random.choice(sources[option])

s = selector([["Adam", "Paul", "Oliver", "Aaron", "Joshua", "Jack"],
              ["Mary", "Sophia", "Emily", "Isobel", "Grace", "Alice", "Lucy"]])

print("Any name: " + next(s))
print("Any girl's name: " + s.send(1))
print("Any boy's name: " + s.send(0))

每次调用send时,它的工作方式都会传递给option变量,该变量用于选择while循环的下一次迭代。

您必须至少调用next一次,然后点击第一个yield语句启动它,但之后它会双向工作(使用{{ 1}}或next表示完整的随机选择,或send(None)表示特定来源。