python - python3中的可迭代类

标签 python python-3.x iterator iterable

我试图实现一个web资源的iterable代理(惰性地获取图像)。
首先,我做了(返回id,在生产中这些将是图像缓冲区)

def iter(ids=[1,2,3]):
    for id in ids:
        yield id

这很好,但现在我需要保持状态。
我读了the four ways to define iterators。我判断迭代器协议是可行的。跟随我的尝试和失败去实现它。
class Test:
    def __init__(me, ids):
         me.ids = ids
    def __iter__(me):
        return me
    def __next__(me):
        for id in me.ids:
            yield id
        raise StopIteration

test = Test([1,2,3])
for t in test:
    print('new value', t)

Output:
new value <generator object Test.__next__ at 0x7f9c46ed1750>
new value <generator object Test.__next__ at 0x7f9c46ed1660>
new value <generator object Test.__next__ at 0x7f9c46ed1750>
new value <generator object Test.__next__ at 0x7f9c46ed1660>
new value <generator object Test.__next__ at 0x7f9c46ed1750>

永远。
发生了什么?
感谢所有人!。

最佳答案

您的__next__方法使用yield,这使它成为一个生成器函数。生成器函数在调用时返回一个新的迭代器。
。。。
Because you wanted to create an iterable, you can just make __next__ the generator here:

class Test:
    def __init__(self, ids):
         self.ids = ids
    def __iter__(self):
        for id in self.ids:
            yield id

请注意,生成器函数不应该使用__next__,只需从函数返回就可以了。
。。当调用__iter__时,Iterables生成迭代器:
Iterable -> (call raise StopIteration) -> Iterator
In the above example, because __iter__ is a generator function, it creates a new object each time we call it:
>>> test = Test([1,2,3])
>>> test.__iter__()  # create an iterator
<generator object Test.__iter__ at 0x111e85660>
>>> test.__iter__()
<generator object Test.__iter__ at 0x111e85740>

。。!Iterables生成迭代器,可以随意创建更多迭代器。这使您可以独立循环它们:
>>> test_it1 = test.__iter__()
>>> test_it1.__next__()
1
>>> test_it2 = test.__iter__()
>>> test_it2.__next__()
1
>>> test_it1.__next__()
2

注意,我对__next__返回的对象调用了__iter__,迭代器,而不是__iter__本身,它没有那个方法,因为它只是一个iterable,而不是迭代器。
迭代器还有一个Test.__iter__方法,它总是必须返回__next__(),因为它们是自己的迭代器。。在引发test.__iter__()之前,每个调用都应返回下一个值。。。
So this is an iterator:
class IteratorTest:
    def __init__(self, ids):
        self.ids = ids
        self.nextpos = 0
    def __iter__(self):
        return self
    def __next__(self):
        if self.ids is None or self.nextpos >= len(self.ids):
            # we are done
            self.ids = None
            raise StopIteration
        value = self.ids[self.nextpos]
        self.nextpos += 1
        return value

。这里的其他回答者使用了看起来更简单的方法,但实际上这些方法包括让其他事情做所有的艰苦工作。当您使用test__iter__时,您正在创建一个不同的迭代器来将self调用委托给。。
在Python代码中通常看不到调用__next____next__的任何东西,因为这两个方法只是可以在Python类中实现的钩子;如果要在C API中实现迭代器,则钩子名称略有不同。相反,您可以使用StopIterationStopIteration函数,或者只使用语法中的对象或接受iterable的函数调用。
。。You can see this if you disassemble the Python bytecode:
>>> from dis import dis
>>> dis("for t in test: pass")
  1           0 LOAD_NAME                0 (test)
              2 GET_ITER
        >>    4 FOR_ITER                 4 (to 10)
              6 STORE_NAME               1 (t)
              8 JUMP_ABSOLUTE            4
        >>   10 LOAD_CONST               0 (None)
             12 RETURN_VALUE

位置2的StopIteration操作码调用StopIterationStopIteration使用结果迭代器上的__next__来保持循环(执行StopIterationiter(self.ids)设置为下一个值,然后跳回位置4),直到(i for i in ids)被提升。。
如果您想更多地了解迭代器和iterables之间的区别,请查看Python标准类型,并查看在它们上使用__next____iter__时会发生什么。如列表或元组:
>>> foo = (42, 81, 17, 111)
>>> next(foo)  # foo is a tuple, not an iterator
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object is not an iterator
>>> t_it = iter(foo)  # so use iter() to create one from the tuple
>>> t_it   # here is an iterator object for our foo tuple
<tuple_iterator object at 0x111e9af70>
>>> iter(t_it)  # it returns itself
<tuple_iterator object at 0x111e9af70>
>>> iter(t_it) is t_it  # really, it returns itself, not a new object
True
>>> next(t_it)  # we can get values from it, one by one
42
>>> next(t_it)  # another one
81
>>> next(t_it)  # yet another one
17
>>> next(t_it)  # this is getting boring..
111
>>> next(t_it)  # and now we are done
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> next(t_it)  # an *stay* done
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> foo  # but foo itself is still there
(42, 81, 17, 111)

您可以使iterable__next__也返回一个自定义迭代器类实例(而不是让generator函数为我们创建迭代器):
class Test:
    def __init__(self, ids):
        self.ids = ids
    def __iter__(self):
        return TestIterator(self)

class TestIterator:
    def __init__(self, test):
        self.test = test
    def __iter__(self):
        return self
def __next__(self):
    if self.test is None or self.nextpos >= len(self.test.ids):
        # we are done
        self.test = None
        raise StopIteration
    value = self.test.ids[self.nextpos]
    self.nextpos += 1
    return value

。。
。对该参数使用不同的名称只会增加与其他有经验的Python开发人员讨论代码的难度。。
(*)当然,除非您的目标是创建迭代器的迭代器(这基本上是iter() iterator所做的,它是生成next()元组的迭代器,但我偏离了方向)。

关于python - python3中的可迭代类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56237021/

相关文章:

python - 在迭代多个 for 循环时创建字典?

python - 如何使用 Python 3 HTTP 库创建 HTTP 代理处理程序

python - 为什么我收到此条件语句的语法错误?

c++ - 如何正确使用enable_if?

c++ - 如何将元素定义为与随机访问迭代器指向的元素类型相同?

c++ - 使用迭代器 C++ 删除 vector 中的重叠字符串

python - 如何覆盖 Django 模型创建而不影响更新?

python - aiohttp:设置每秒最大请求数

python - 处理 : 时遇到错误

python - 属性错误: module 'math' has no attribute '_name_'