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

时间:2016-10-28 02:51:11

标签: python arguments iterable built-in

让我们考虑一个文件:

$ 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 .

当我想逐个阅读文件时,我可以https://stackoverflow.com/a/25071590/610569

>>> 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循环可能看起来有点unpythonic esp。当我使用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#iterhttps://docs.python.org/3.6/library/functions.html#iter

根据文件:

  

返回一个迭代器对象。根据第二个参数的存在,第一个参数的解释非常不同。如果没有第二个参数,object必须是支持迭代协议的集合对象( iter ()方法),或者它必须支持序列协议( getitem ()整数参数从0开始的方法。如果它不支持这些协议中的任何一个,则引发TypeError。如果给出第二个参数sentinel,则object必须是可调用对象。在这种情况下创建的迭代器将为每个对 next ()方法的调用调用没有参数的对象;如果返回的值等于sentinel,则会引发StopIteration,否则返回值。

我想文档需要一些&#34;解密&#34;:

  • 没有第二个参数,object必须是支持迭代协议的集合对象( iter ()方法)

这是否意味着它需要来自collections?或者只要对象有一个__iter__(),那还可以吗?

  • ,或者它必须支持序列协议( getitem ()方法,整数参数从0开始)

那相当神秘。那么这是否意味着它试图查看序列是否被索引并因此可查询并且索引必须从0开始?它是否也意味着指数需要是连续的,即0,1,2,3 ......,而不是0,2,8,13 ......?

  • 如果它不支持这些协议,则会引发TypeError。

是的,这一部分,我明白=)

  • 如果给出第二个参数sentinel,则object必须是可调用对象。

好的,现在这会得到一点科幻小说。它只是Python中的一个术语,可以称之为sentinel吗? sentinel的含义是什么?和&#34;可调用对象&#34;喜欢它的功能而不是类型对象?

  • 在这种情况下创建的迭代器将为每个对next()方法的调用调用没有参数的对象;

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

  • 如果返回的值等于sentinel,则会引发StopIteration,否则返回该值。

好的,这里sentinel指的是一些违规标准?

有人可以帮助解读/澄清关于iter的上述观点的含义吗?

1 个答案:

答案 0 :(得分:5)

使用一个参数时,必须为iter提供一个具有__iter__特殊方法, __getitem__特殊方法的对象。如果它们都不存在,iter 引发错误

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

有两种迭代协议。旧协议依赖于调用__getitem__从0开始的连续整数,直到引发IndexError的整数。新协议依赖于从__iter__返回的迭代器。

在Python 2中,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>

要使自定义类可迭代,您需要 __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__的对象。第一个参数不带参数调用;返回值是从迭代器中产生的。当从该迭代的函数调用返回的值等于给定的 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]

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

>>> 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(),如果返回的值不等于6,则会产生。{/ p>

或者

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

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

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

如果还没有在文件末尾,则会返回一个字符。

这可以用来从重复的函数调用中创建无限的迭代器:

>>> dice = iter(throw, 7)

由于返回的值永远不能等于7,因此迭代器将永远运行。一个常见的习惯用法是使用匿名object来确保比较对于任何可以想象的值都不会成功

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

由于

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

请注意,单词 sentinel 通常用于在某些数据中用作结束标记的值,并且在数据中不会自然发生,如{{3} }。