将Generator转换为Iterator类的最佳方法

时间:2018-07-18 08:35:35

标签: python iterator generator

考虑以下虚拟示例:

def common_divisors_generator(n, m):

    # Init code
    factors_n = [i for i in range(1, n + 1) if n%i == 0]
    factors_m = [i for i in range(1, m + 1) if m%i == 0]

    # Iterative code
    for fn in factors_n:
        for fm in factors_m:
            if fn == fm:
                yield fn

# The next line is fast because no code is executed yet
cdg = common_divisors_generator(1537745, 373625435)
# Next line is slow because init code is executed on first iteration call
for g in cdg:
    print(g)

一旦生成器第一次被迭代(与生成器初始化时相反),就会执行花费很长时间来计算的初始化代码。我希望在生成器初始化时执行它的init代码。

为此,我将生成器转换为迭代器类,如下所示:

class CommonDivisorsIterator(object):

    def __init__(self, n, m):
        # Init code
        self.factors_n = [i for i in range(1, n + 1) if n%i == 0]
        self.factors_m = [i for i in range(1, m + 1) if m%i == 0]

    def __iter__(self):
        return self

    def __next__(self):
        # Some Pythonic implementation of the iterative code above
        # ...
        return next_common_divisor

与生成器中带有__next__关键字的迭代代码的简单性相比,我能想到的实现上述yield方法的所有方式都非常麻烦。

在迭代器类中实现__next__方法的最Python方式是什么?

或者,如何修改生成器,以便在初始化时执行初始化代码?

1 个答案:

答案 0 :(得分:4)

在两种情况下(无论使用函数还是类),解决方案都是将实现分为两个函数:设置函数和生成器函数。

在函数中使用yield会将其转换为生成器函数,这意味着它在被调用时会返回生成器。但是,即使不使用yield,也无法阻止您创建生成器并返回它,就像这样:

def common_divisors_generator(n, m):
    factors_n = [i for i in range(1, n + 1) if n%i == 0]
    factors_m = [i for i in range(1, m + 1) if m%i == 0]

    def gen():
        for fn in factors_n:
            for fm in factors_m:
                if fn == fm:
                    yield fn

    return gen()

如果您使用的是类,则无需实现__next__方法。您可以在yield方法中使用__iter__

class CommonDivisorsIterator(object):
    def __init__(self, n, m):
        self.factors_n = [i for i in range(1, n + 1) if n%i == 0]
        self.factors_m = [i for i in range(1, m + 1) if m%i == 0]

    def __iter__(self):
        for fn in self.factors_n:
            for fm in self.factors_m:
                if fn == fm:
                    yield fn