【问题标题】:Implement a reversible iteration class to replace a generator实现一个可逆迭代类来替换一个生成器
【发布时间】:2019-08-14 17:42:24
【问题描述】:

在 python 中,我有一个类,它有一个返回生成器的方法:

class foo():
    data = [1, 2, 3]
    def mygen(self):
        for d in self.data:
            yield d

instance = foo()
print([i for i in instance.mygen()])

但我无法扭转这一点:

print([i for i in reversed(instance.mygen())])
TypeError: 'generator' object is not reversible

所以我想我可以实现一个在调用__iter__ 时返回生成器的类,像这样

class foo():
    data = [1, 2, 3]
    def mygen(self):
        return _ReversibleIterator(self)

class _ReversibleIterator(object):
    def __init__(self, obj):
        self.obj = obj

    def __iter__(self):
        for d in obj.data:
            yield d

    def __reversed__(self):
        for d in reversed(obj.data):
            yield d

但我认为这不太一样,因为_ReversibleIterator 类没有next() 方法。

那么创建返回迭代器的类方法的pythonic方法是什么?可以是reversed()

(显然我只是以[1,2,3] 为例。真正要迭代的东西是不那么简单可逆的)

【问题讨论】:

    标签: python iterator generator


    【解决方案1】:

    根据docsreversed 必须使用以下两种方法之一:__reversed__ 方法或__len____getitem__ 方法。如果您考虑一下,这是有道理的,因为大多数生成器不能支持reversed,因为它们会即时生成结果:它们不知道下一个元素是什么,更不用说最后一个元素了成为。但是,如果您知道它的长度并且可以随机访问任何索引,则可以反转它。

    class foo():
        data = [1, 2, 3]
        def mygen(self):
            return _ReversibleIterator(self)
    
    class _ReversibleIterator(object):
        def __init__(self, obj):
            self.obj = obj
            self.index = 0
    
        def __iter__(self):
            self.index = 0
            return self
    
        def __reversed__(self):
            return reversed(self.obj.data)
    
        def __next__(self):
            try:
                el = self.obj.data[self.index]
            except IndexError:
                raise StopIteration
            self.index += 1
            return el
    

    class _ReversibleIterator(object):
        def __init__(self, obj):
            self.obj = obj
            self.index = 0
    
        def __iter__(self):
            self.index = 0
            return self
    
        def __len__(self):
            return len(self.obj.data)
    
        def __getitem__(self, i):
            return self.obj.data[i]
    
        def __next__(self):
            try:
                el = self[self.index]
            except IndexError:
                raise StopIteration
            self.index += 1
            return el
    

    顺便说一句,如果您愿意,可以将for d in whatever: yield d 替换为yield from whatever

    【讨论】:

    • 谢谢! (我刚刚编辑了问题以添加 __reversed__ 方法,我之前忘记了)。但即使使用这些方法,返回的对象也完全相同,因为它没有next() 方法。在简单的生成器情况下,我可以执行a = instance.mygen(); print(next(a)),但是当我返回_ReversibleIterator 时,next 会产生错误:TypeError: '_ReversibleIterator' object is not an iterator。
    • @user2667066 通常,迭代器在__next__ 方法中实现实际的生成部分,所有__iter__ 所做的只是返回self。我可以进行编辑。
    • 谢谢,这很有用。但这是否意味着我不能简单地在__iter__ 部分使用生成器?
    • @user2667066 你可以,但你不会有__next__。另外,请问您是否有能力为您的用例实现__len____getitem__
    • @user2667066 顺便说一句,如果你真的想要next功能,如果你将a = instance.mygen()更改为@987654345,你不需要在你的类中添加任何东西@。这可以接受吗?
    猜你喜欢
    • 2020-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    • 2013-08-09
    • 1970-01-01
    相关资源
    最近更新 更多