python - 自定义迭代器类与生成器?

标签 python python-3.x iterator generator

下面是一个说明。我想总体了解当可以定义生成器函数时是否有理由定义自定义迭代器类。

我需要迭代一个序列,将每个元素转换为 int,例如

# seq is a sequence of strings or in general anything convertible to int
def f(seq):
    # ...
    g(int_iter(seq))

# iseq is a numeric sequence
def g(iseq):
    it = iter(iseq)
    # ...

我可以使用自定义迭代器类:

# iterator converting elements it iterates over to int
class int_iter:
    def __init__(self, iterable):
        self.it = iter(iterable)

    def __iter__(self):
        return self

    def __next__(self):
        return int(next(self.it))

或生成器函数:

def int_iter(seq):
    return (int(i) for i in seq)

这些解决方案总是可以互换的吗?
它们是否同等有效(时间和空间方面)?
从风格上来说,他们中的哪一个被认为更好?

谢谢!

最佳答案

如果我要将其写为答案,那么让我们添加一些示例来演示差异。假设我们有一个简单的迭代,例如:

source_list = list(range(10))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

如果你想将其转换为字符串列表,除了已经提到的为此目的而设计的 map() 之外,还有很多方法 - 你可以做一个简单的生成器:

def gen_str(iterable):  # this is equivalent to returning an in-line generator
    for element in iterable:
        yield str(element)

test_gen = gen_str(source_list)
for element in test_gen:
    print(repr(element), end=" ")
# '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 

或者你可以编写一个完整的迭代器类:

class iter_str(object):

    def __init__(self, iterable):
        self._iterable = iterable
        self._iterator = self._get_iter(self._iterable)

    def __iter__(self):
        return self

    def __next__(self):
        return str(next(self._iterator))

    @staticmethod
    def _get_iter(iterable):  # a generator for forward iteration
        for element in iterable:
            yield element

test_iter = iter_str(source_list)
for element in test_iter:
    print(repr(element), end=" ")
# '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 

到目前为止,它们是相同的 - 但是如果您想在迭代时跳过一些元素会发生什么?您不能指示生成器这样做,并且为了跳过元素,您需要在迭代代码本身中添加按耗尽跳过逻辑,即:

test_gen = gen_str(source_list)
for element in test_gen:
    if element == "5":
        for _ in range(3):
            next(test_gen)
        continue
    print(repr(element), end=" ")
# '0' '1' '2' '3' '4' '9' 

另一方面,使用迭代器类,您可以通过添加简单的 skip() 方法来封装控件,例如:

def skip(self, elements=1):
    for _ in range(elements):
        next(self._iterator)

然后你可以优雅地做同样的事情:

test_iter = iter_str(source_list)
for element in test_iter:
    if element == "5":
        element = test_iter.skip(3)
        continue
    print(repr(element), end=" ")
# '0' '1' '2' '3' '4' '9' 

但这只是冰山一角 - 如果您想在迭代过程中停止生成字符串并使用原始数据,会发生什么?没有办法通知生成器这样做(除非您通过传递一些外部控制变量来构建它),而对迭代器类的简单更改就可以让您做到这一点:

class iter_str(object):

    def __init__(self, iterable, string_mode=True):
        self._iterable = iterable
        self.string_mode = string_mode
        self._iterator = self._get_iter(self._iterable)

    def __iter__(self):
        return self

    def __next__(self):
        element = next(self._iterator)
        if self.string_mode:
            return str(element)
        return element

    @staticmethod
    def _get_iter(iterable):  # a generator for forward iteration
        for element in iterable:
            yield element

test_iter = iter_str(source_list)
for element in test_iter:
    if element == "4":
        test_iter.string_mode = False
    print(repr(element), end=" ")
# '0' '1' '2' '3' '4' 5 6 7 8 9 

通过这种方式,您可以对迭代添加任意控制,包括反转、重复迭代,甚至在迭代中途切换迭代器源等。简单的生成器不允许您在没有一些重大麻烦的情况下执行任何操作.

至于效率,从这个例子中可以明显看出,生成器的效率更高,因为我们无论如何都依赖于内部生成器,但是如果您需要控制可迭代对象的生命周期,那么性能损失将很快消失。 d 必须添加更复杂的检查,并且通常会使您的生活在尝试解决发电机限制时变得痛苦。

我不会评论风格,但我想说,一般来说,最好使用最好的工具来完成工作 - 如果您不需要可迭代的生命周期控制,请继续使用生成器,如果你这样做的话 - 迭代器类是一个不错的选择。

关于python - 自定义迭代器类与生成器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47737636/

相关文章:

python - ValueError : Dimensions must be equal,,但“softmax_cross_entropy_with_logits_sg”的值为 2 和 3799

c++ - C++ 映射删除循环中的元素会使迭代器无效吗? (C++03)

python - Django:通过外键查找所有反向引用

python - Django - 过滤模型对象

python 3.5 matplotlib从csv读取日期但不能表示为X轴上的日期

python-3.x - 无法在 M1 Mac 上使用 Pip 安装 OpenCV

c++ - 同一个类中的迭代器和 const_iterator 的问题

python - 将 yield 包含在另一个函数中

python - Docker,Celery,组合方法失败

python - 使用 python selenium 单击查看联系人后,我无法获取相同的检查输出,从而无法抓取手机号码