【问题标题】:How should I create an iterator that also supports a tell() method?我应该如何创建一个也支持 tell() 方法的迭代器?
【发布时间】:2015-09-01 04:56:25
【问题描述】:

我需要创建一个类,它使用迭代器协议从数据流中一次返回一个字符,但它也有一个类似于文件对象的tell() 的方法,返回它当前在流。

基本上,这就是我需要的:

>>> x = MyIterator('abcdefghijklmnopqrstuvwxyz')
>>> x.tell()
0
>>> next(x)
'a'
>>> next(x)
'b'
>>> next(x)
'c'
>>> x.tell()
3
>>> next(x)
'd'
>>> x.tell()
4

最pythonic的方法是什么?我可以以某种方式使用生成器来做到这一点,还是必须创建一个“手动”实现迭代器协议的类? (即具有__next__() 方法的类,当数据用完时手动引发StopIteration)

请注意,这个问题有点简化了我的实际需求。我实际上需要tell()-analogue 来返回流位置的函数而不仅仅是位置,所以请不要告诉我“只是让我的迭代器的调用者使用enumerate()”或类似的东西。

【问题讨论】:

  • 您也应该告诉我们您的实际需求,没有实际需求,我们只能提供简化版的解决方案。实际要求是否仍然只是一个字符串,就像您的简化版本中给出的那样?
  • 我正在写一个反汇编程序。我正在反汇编的体系结构具有指令指针相关地址;要正确反汇编这些,有必要知道当前的 IP(这是流中位置的数学函数,但会根据被反汇编的图像而变化,所以我不能将其烘焙到反汇编器中)跨度>

标签: python iterator generator


【解决方案1】:

我可以用生成器来做吗

不是真的。 (理论上,您可以通过将计数器作为函数属性存储在生成器函数上来做到这一点,但试图让函数引用自身是一个冒险的提议。)

还是我必须创建一个“手动”实现迭代器协议的类? (即具有__next__() 方法的类,当数据用完时会手动引发 StopIteration)

是的,不是的。是的,您必须创建一个迭代器类,但这并不意味着您必须手动引发 StopIteration。假设您希望您的迭代器包装一些可迭代的源,那么您可以依赖该源来引发 StopIteration。这是一个简单的例子:

class MyIter(object):
    def __init__(self, source):
        self.source = iter(source)
        self.counter = 0

    def next(self):
        self.counter += 1
        return next(self.source)

    def __iter__(self):
        return self

    def tell(self):
        return self.counter

(这使用next a la Python 2;对于 Python 3,您必须将 def next 更改为 def __next__。)

然后:

>>> x = MyIter('abc')
>>> for item in x:
...     print(item, x.tell())
a 1
b 2
c 3

【讨论】:

  • @AlexJackson:“是__iter__”是什么意思?
  • 抱歉,我不小心发布了不完整的评论,然后没时间编辑它。对于我的目的,tell() 不一定是迭代器本身的方法,它只需要访问迭代器的状态。如果不是实现next(self),MyIter 类__iter__(self) 方法是一个生成器,它增加self.counter 并从self.source 产生怎么办? (我相信这会使 MyIter 成为“可迭代”而不是“迭代器”......我刚刚开始掌握它们之间的区别)这是否也会自动处理源的耗尽?
  • @AlexJackson:是的,这也可以,尽管与上述版本相比,它并没有为您节省太多。它基本上只是意味着您将next() 的内容放入__iter__()。是的,它将MyIter 是一个可迭代但不是迭代器。 (在上面的版本中,两者都是。)
  • 好吧,我想避免对 python 2 和 python 3 使用不同的语法,但我刚刚意识到,由于 str 与 bytes 的事情,我无论如何都必须这样做。如果我让next(self) 返回ord(next(self.source)) 和__next__(self) 返回next(self.source),它会用一块石头杀死两只鸟。可迭代版本也有一个缺点,我也刚刚意识到,在同一个 MyIter 对象上调用 iter() 两次将创建两个共享相同状态的迭代器,而不是对相同数据的两个独立迭代器,即.. . 不直观。
【解决方案2】:

如果计算它不是太昂贵,让迭代器产生值和位置。或者,提供两种迭代方法,一种只产生值,另一种产生值和位置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-01
    • 2018-05-20
    • 2016-07-24
    • 2010-10-29
    • 2021-10-15
    • 2021-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多