Python 中 iter 函数的第二个参数是什么?

What is the 2nd argument for the iter function in Python?

让我们考虑一个文件:

$ echo -e """This is a foo bar sentence .\nAnd this is the first txtfile in the corpus .""" > test.txt
$ cat test.txt 
This is a foo bar sentence .
And this is the first txtfile in the corpus .

当我想按字符读取文件时,我可以这样做 :

>>> fin = open('test.txt')
>>> while fin.read(1):
...     fin.seek(-1,1)
...     print fin.read(1),
... 
T h i s   i s   a   f o o   b a r   s e n t e n c e   . 
A n d   t h i s   i s   t h e   f i r s t   t x t f i l e   i n   t h e   c o r p u s   .

但是使用 while 循环可能看起来有点不符合 Python 风格,尤其是。当我使用 fin.read(1) 检查 EOF 然后回溯以读取当前字节时。所以我可以做这样的事情 How to read a single character at a time from a file in Python?:

>>> import functools
>>> fin = open('test.txt')
>>> fin_1byte = iter(functools.partial(fin.read, 1), '')
>>> for c in fin_1byte:
...     print c,
... 
T h i s   i s   a   f o o   b a r   s e n t e n c e   . 
A n d   t h i s   i s   t h e   f i r s t   t x t f i l e   i n   t h e   c o r p u s   .

但是当我在没有第二个参数的情况下尝试它时,它会抛出一个 TypeError:

>>> fin = open('test.txt')
>>> fin_1byte = functools.partial(fin.read, 1)
>>> for c in iter(fin_1byte):
...     print c,
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'functools.partial' object is not iterable

iter中的第二个参数是什么? 文档也不多说:https://docs.python.org/2/library/functions.html#iter and https://docs.python.org/3.6/library/functions.html#iter


根据文档:

Return an iterator object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, object must be a collection object which supports the iteration protocol (the iter() method), or it must support the sequence protocol (the getitem() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised. If the second argument, sentinel, is given, then object must be a callable object. The iterator created in this case will call object with no arguments for each call to its next() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.

我想文档需要一些 "decrypting":

这是否意味着它需要来自 collections?还是只要对象有一个__iter__()就可以了?

这有点神秘。那么这是否意味着它会尝试查看序列是否已编入索引并因此可查询,并且索引必须从 0 开始?这是否也意味着索引需要是连续的,即 0、1、2、3,...而不是像 0、2、8、13,...?

是的,这部分我明白=)

好的,现在这有点科幻了。将某物称为 sentinel 只是 Python 中的一个术语吗? sentinel 是什么意思 Python? "callable object" 就像它是一个函数而不是类型对象?

这部分我不太明白,也许一个例子会有所帮助。

好吧,所以这里的sentinel指的是一些打破标准?

谁能帮忙解答一下decipher/clarify以上几点关于iter的意思?

对于一个参数,iter 必须被赋予一个具有 __iter__ 特殊方法、 __getitem__ 特殊方法的对象。如果两者都不存在,iter 引发错误

>>> iter(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not iterable

有 2 个迭代协议。旧协议依赖于调用 __getitem__ 来获取从 0 到引发 IndexError 的连续整数。新协议依赖于 return 从 __iter__ 编辑而来的迭代器。

在Python2中,str__iter__特殊方法都没有:

Python 2.7.12+ (default, Sep 17 2016, 12:08:02) 
[GCC 6.2.0 20160914] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 'abc'.__iter__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__iter__'

但它仍然是可迭代的:

>>> iter('abc')
<iterator object at 0x7fcee9e89390>

要使您的自定义 class 可迭代,您需要 __iter____getitem__ 引发 IndexError不存在的项目:

class Foo:
    def __iter__(self):
        return iter(range(5))

class Bar:
    def __getitem__(self, i):
        if i >= 5:
            raise IndexError
        return i

使用这些:

>>> list(iter(Foo()))
[0, 1, 2, 3, 4]
>>> list(iter(Bar()))
[0, 1, 2, 3, 4]

通常不需要显式 iter,因为 for 期望 iterables 的循环和方法将隐式创建迭代器:

>>> list(Foo())
[0, 1, 2, 3, 4]
>>> for i in Bar():
0
1
2
3
4

对于 2 参数形式,第一个参数必须是实现 __call__ 的函数或对象。第一个参数在没有参数的情况下被调用; return 值是从迭代器中产生的。当该次迭代的函数调用的值 return 等于给定的 sentinel 值时,迭代停止,就好像通过:

value = func()
if value == sentinel:
    return
else:
    yield value

例如,要在骰子 之前 获得值,我们抛出 6,

>>> import random
>>> throw = lambda: random.randint(1, 6)
>>> list(iter(throw, 6))
[3, 2, 4, 5, 5]
>>> list(iter(throw, 6))
[1, 3, 1, 3, 5, 1, 4]

为了进一步说明,每次在迭代器上使用 next() 时,都会在不带参数的情况下调用给定函数(或具有 __call__ 特殊方法的给定对象):

>>> def throw_die():
...     die = random.randint(1, 6)
...     print("returning {}".format(die))
...     return die
...
>>> throws = iter(throw_die, 6)
>>> next(throws)
returning 2
2
>>> next(throws)
returning 4
4
>>> next(throws)
returning 6
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

(即 throw 被称为 throw(),如果 returned 值不等于 6,则产生它。

或者在

的情况下
>>> fin_1byte = iter(functools.partial(fin.read, 1), '')
>>> for c in fin_1byte:
...     print c,

从文件末尾的文件中读取 returns 空字符串(如果是二进制文件,则为空字节):

>>> from io import StringIO
>>> fin = StringIO(u'ab')
>>> fin.read(1)
u'a'
>>> fin.read(1)
u'b'
>>> fin.read(1)
u''

如果还没有到达文件末尾,将return编辑一个字符。

这也可用于从重复的函数调用中生成无限迭代器:

>>> dice = iter(throw, 7)

因为值 returned 永远不可能等于 7,迭代器永远运行。一个常见的习惯用法是使用匿名 object 来确保比较不会对任何可能的值

成功
>>> dice = iter(throw, object())

因为

>>> object() != object()
True

请注意,单词 sentinel 通常用于在某些数据中用作结束标记的值,并且不会在数据中自然出现,因为在 this Java answer.