python中iterable_python3中的Iterable类

您的__next__方法使用yield,这使它成为生成器函数。生成器函数在调用时返回新的迭代器。在

但是__next__方法是迭代器接口的一部分。它本身不应该是迭代器。__next__应该返回下一个值,而不是返回所有值的值(*)。在

因为您想创建一个iterable,所以可以在这里将__iter__作为生成器:class Test:

def __init__(self, ids):

self.ids = ids

def __iter__(self):

for id in self.ids:

yield id

请注意,生成器函数不应使用raise StopIteration,只需从函数返回即可。在

上面的类是iterable。Iterables只有一个__iter__方法,和没有__next__方法。当调用__iter__时,Iterables生成一个迭代器:

Iterable->(调用__iter__)->迭代器

在上面的例子中,因为Test.__iter__是一个生成器函数,所以每次调用它时它都会创建一个新对象:

^{pr2}$

生成器对象是一种特定类型的迭代器,通过调用生成器函数或使用生成器表达式创建。注意,表示中的十六进制值不同,为这两个调用创建了两个不同的对象。这是故意的!Iterables生成迭代器,并可以随意创建更多的迭代器。这使您可以独立地循环它们:>>> test_it1 = test.__iter__()

>>> test_it1.__next__()

1

>>> test_it2 = test.__iter__()

>>> test_it2.__next__()

1

>>> test_it1.__next__()

2

请注意,我对test.__iter__()返回的对象调用了__next__(),迭代器,而不是{}本身,它没有这个方法,因为它只是一个iterable,不是迭代器。在

迭代器还有一个__iter__方法,它总是必须返回self,因为它们是它们自己的迭代器。正是__next__方法使它们成为迭代器,__next__的作业将被反复调用,直到它引发{}。在引发StopIteration之前,每个调用都应返回下一个值。一旦迭代器完成(引发了StopIteration),它就意味着总是引发StopIteration。迭代器只能使用一次,除非它们是无限的(永远不要引发StopIteration,只在每次调用__next__时继续生成值)。在

这是一个迭代器: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

这需要做更多的工作;它必须跟踪要生成的下一个值是什么,以及我们是否已经提出了StopIteration。这里的其他回答者使用了看起来更简单的方法,但实际上这些方法涉及到让其他东西做所有的艰苦工作。当您使用iter(self.ids)或(i for i in ids)时,您正在创建一个不同的迭代器来委托__next__调用。这有点欺骗,将迭代器的状态隐藏在现成的标准库对象中。在

在Python代码中通常看不到任何调用__iter__或__next__的东西,因为这两个方法只是可以在Python类中实现的钩子;如果要在C API中实现迭代器,那么钩子的名称就略有不同。相反,您可以使用^{}和^{}函数,或者只使用语法中的对象或接受iterable的函数调用。在

for循环就是这样的语法。当您使用for循环时,Python使用(道德上等价的)对对象调用__iter__(),然后对结果迭代器对象调用__next__()来获取每个值。如果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处的GET_ITER操作码调用test.__iter__(),FOR_ITER使用结果迭代器上的__next__继续循环(执行STORE_NAME将{}设置为下一个值,然后跳回位置4),直到StopIteration被引发。一旦发生这种情况,它将跳到位置10结束循环。在

如果您想更多地了解迭代器和iterables之间的区别,请看一下Python标准类型,看看在它们上使用iter()和{}时会发生什么。像列表或元组:>>> foo = (42, 81, 17, 111)

>>> next(foo) # foo is a tuple, not an iterator

Traceback (most recent call last):

File "", line 1, in

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

>>> iter(t_it) # it returns itself

>>> 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 "", line 1, in

StopIteration

>>> next(t_it) # an *stay* done

Traceback (most recent call last):

File "", line 1, in

StopIteration

>>> foo # but foo itself is still there

(42, 81, 17, 111)

您可以使iterable的Test也返回一个自定义迭代器类实例(而不是havi跳出)ng 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

这很像上面原始的IteratorTest类,但是TestIterator保留了对Test实例的引用。这就是tuple_iterator的工作原理。在

关于命名约定的最后一点简短说明:我坚持使用self作为方法的第一个参数,因此绑定实例。对该参数使用不同的名称只会增加与其他经验丰富的Python开发人员谈论代码的难度。不要使用me,不管它看起来多么可爱或短。在

当然,除非您的目标是创建一个迭代器的迭代器(这基本上就是^{} iterator所做的,它是一个生成(object, group_iterator)元组的迭代器,但我离题了)。在

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值