【问题标题】:Creating an iterator and choose among several sources of iteration创建一个迭代器并在多个迭代源中进行选择
【发布时间】:2016-07-13 23:24:39
【问题描述】:

我想创建一个迭代器,它有多个对象“源”进行迭代。我希望能够为其__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 成为真正的Iterator,那我当然想写:

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

但出现错误(2d 行):

TypeError: next() takes no keyword arguments

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

最后,有没有办法实现这样的Iterator?

【问题讨论】:

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


    【解决方案1】:

    编写一个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 语句,但之后它会以两种方式工作(使用nextsend(None) 以获得完整的随机选择,或 send(option) 用于特定来源。

    【讨论】:

    • 好的,所以不可能使用__next__这样调用它:next(s, choice=1)?
    • 这不是迭代器协议(或 next 内置)的定义方式,您无法更改该定义,所以不。但是send 有效,我不知道你为什么特别坚持使用next
    猜你喜欢
    • 2016-07-24
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 2018-05-06
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    相关资源
    最近更新 更多