iter(callable,sentinel)有什么用途?

时间:2016-06-28 22:41:24

标签: python

所以,我正在观看Raymond Hettinger的演讲Transforming Code into Beautiful, Idiomatic Python,他提出了iter这种我从未意识到的形式。他的例子如下:

而不是:

blocks = []
while True:
    block = f.read(32)
    if block == '':
        break
    blocks.append(block)

使用:

blocks = []
read_block = partial(f.read, 32)
for block in iter(read_block, ''):
    blocks.append(block)

在检查iter的{​​{3}}后,我找到了一个类似的例子:

with open('mydata.txt') as fp:
    for line in iter(fp.readline, ''):
        process_line(line)

这看起来对我非常有用,但我想知道你是否Pythonistas知道这个构造的任何例子并不涉及I / O读取循环?也许在标准库中?

我可以想到非常人为的例子,如下所示:

>>> def f():
...     f.count += 1
...     return f.count
... 
>>> f.count = 0
>>> list(iter(f,20))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> 

但显然这对内置迭代来说没有任何用处。此外,当您将状态分配给函数时,似乎代码闻到了我。那时候,我可能应该和一个班级一起工作,但是如果我要编写一个类,我也可以为我想要完成的任何事情实现迭代器协议。

3 个答案:

答案 0 :(得分:9)

这是我提出的一个愚蠢的例子:

from functools import partial
from random import randint

pull_trigger = partial(randint, 1, 6)

print('Starting a game of Russian Roulette...')
print('--------------------------------------')

for i in iter(pull_trigger, 6):
    print('I am still alive, selected', i)

print('Oops, game over, I am dead! :(')

示例输出:

$ python3 roulette.py 
Starting a game of Russian Roulette...
--------------------------------------
I am still alive, selected 2
I am still alive, selected 4
I am still alive, selected 2
I am still alive, selected 5
Oops, game over, I am dead! :(

我们的想法是拥有一个产生随机值的生成器,并且您希望在选择特定值后进行处理。你可以,例如在模拟的每次运行中使用此模式,试图确定随机过程的平均结果。

当然,你要建模的过程可能会比简单的骰子滚动更复杂......

我能想到的另一个例子是重复执行操作,直到成功为止,由空错误消息表示(我们假设某些第三方功能的设计与此类似,而不是例如使用异常):

from foo_lib import guess_password

for msg in iter(guess_password, ''):
    print('Incorrect attempt, details:', msg)

# protection cracked, continue...

答案 1 :(得分:6)

作为一项规则,我见过的两个主要用途包括将类似于C API的函数(隐式状态,没有迭代的概念)转换为迭代器。类似文件的对象是一个常见的例子,但它出现在其他库中,很难包装C API。您期望的模式可以在FindFirstFile / FindNextFile等API中看到,其中资源被打开,每个调用都会提升内部状态并返回新值或标记变量(如{{ C)中的1}}。将它包装在实现迭代器协议的类中通常是最好的,但是如果你必须自己做,虽然API是内置的C级,但是包装最终会减慢使用速度,其中两个arg iter,在C中实现好吧,可以避免额外的字节码执行费用。

其他示例涉及在循环期间更改的可变对象,例如,在bytearray中以相反顺序循环,仅在处理完成后删除该行:

NULL

另一种情况是以渐进的方式使用切片,例如,一种有效的(如果难以置信的丑陋)方法将可迭代分组为>>> from functools import partial >>> ba = bytearray(b'aaaa\n'*5) >>> for i in iter(partial(ba.rfind, b'\n'), -1): ... print(i) ... ba[i:] = b'' ... 24 19 14 9 4 项组,同时允许最终组小于{{1如果输入可迭代的长度不是n个项目的偶数倍,那么这个项目(我实际使用的是这个项目,但我通常使用n而不是两个arg n) :

itertools.takewhile(bool

另一个用途:将多个pickle对象写入单个文件,然后是sentinel值(例如iter),所以当unpickling时,你可以使用这个成语而不是需要以某种方式记住pickle项目的数量,或需要一遍又一遍地致电# from future_builtins import map # Python 2 only from itertools import starmap, islice, repeat def grouper(n, iterable): '''Returns a generator yielding n sized tuples from iterable For iterables not evenly divisible by n, the final group will be undersized. ''' # Keep islicing n items and converting to groups until we hit an empty slice return iter(map(tuple, starmap(islice, repeat((iter(iterable), n)))).__next__, ()) # Use .next instead of .__next__ on Py2 ,直到None

load

答案 2 :(得分:2)

在多处理/多线程代码中,您(希望)会经常发现此构造用于轮询队列或管道。在标准库中,您还可以在multiprocessing.Pool中找到它:

@staticmethod
def _handle_tasks(taskqueue, put, outqueue, pool, cache):
    thread = threading.current_thread()

    for taskseq, set_length in iter(taskqueue.get, None):
        task = None
        try:
            # iterating taskseq cannot fail
            for task in taskseq:
        ...
    else:
        util.debug('task handler got sentinel')

前段时间,我遇到了this博客条目,IMO总结了iter(callable, sentinel)优于while True ... break的优势:

  

通常,当我们遍历对象或直到条件发生时,我们在循环的第一行中了解循环的范围。例如,当读取以书中的书开始的循环时,我们意识到我们正在遍历所有书。当我们看到一个循环而不是battery.empty()开始时,我们意识到该循环的范围只要有电池就可以了。   当我们说“永远做”(即为True)时,很明显,这个范围是 lie 。因此,这需要我们牢牢把握住这个想法,并在代码的其余部分中搜索一条语句,以使我们摆脱困境。我们进入信息较少的循环,因此其可读性较低。