所以,我正在观看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]
>>>
但显然这对内置迭代来说没有任何用处。此外,当您将状态分配给函数时,似乎代码闻到了我。那时候,我可能应该和一个班级一起工作,但是如果我要编写一个类,我也可以为我想要完成的任何事情实现迭代器协议。
答案 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 。因此,这需要我们牢牢把握住这个想法,并在代码的其余部分中搜索一条语句,以使我们摆脱困境。我们进入信息较少的循环,因此其可读性较低。