iter(可调用,哨兵)的用途是什么?

jua*_*aga 17 python

所以,我正在观看Raymond Hettinger的演讲,将代码转换为美丽的,惯用的Python,并且他提出了iter我从未意识到的这种形式.他的例子如下:

代替:

blocks = []
while True:
    block = f.read(32)
    if block == '':
        break
    blocks.append(block)
Run Code Online (Sandbox Code Playgroud)

使用:

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

检查完文档iter,我发现了一个类似的例子:

with open('mydata.txt') as fp:
    for line in iter(fp.readline, ''):
        process_line(line)
Run Code Online (Sandbox Code Playgroud)

这看起来对我很有用,但我想知道你是否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]
>>> 
Run Code Online (Sandbox Code Playgroud)

但显然这对于​​内置的iterables来说并不是更有用.此外,当您将状态分配给函数时,似乎代码闻到了我.那时,我可能应该使用一个类,但如果我要编写一个类,我不妨实现迭代器协议,无论我想要完成什么.

pla*_*mut 10

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

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! :(')
Run Code Online (Sandbox Code Playgroud)

样本输出:

$ 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! :(
Run Code Online (Sandbox Code Playgroud)

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

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

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

from foo_lib import guess_password

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

# protection cracked, continue...
Run Code Online (Sandbox Code Playgroud)


Sha*_*ger 6

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

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

另一种情况是以渐进方式使用切片,例如,一种有效的(如果难以置信的丑陋)方式将可迭代分组为n项目组,同时n如果输入可迭代不是偶数,则允许最终组小于项目n长度的项目(这个我实际上使用过,虽然我通常使用itertools.takewhile(bool而不是两个arg iter):

# 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
Run Code Online (Sandbox Code Playgroud)

另一个用途:将多个pickle对象写入一个文件,然后是一个sentinel值(None例如),所以当unpickling时,你可以使用这个成语而不是需要以某种方式记住pickle项目的数量,或者需要load反复调用直到EOFError:

with open('picklefile', 'rb') as f:
    for obj in iter(pickle.Unpickler(f).load, None):
        ... process an object ...
Run Code Online (Sandbox Code Playgroud)


Dar*_*aut 6

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

\n\n
@staticmethod\ndef _handle_tasks(taskqueue, put, outqueue, pool, cache):\n    thread = threading.current_thread()\n\n    for taskseq, set_length in iter(taskqueue.get, None):\n        task = None\n        try:\n            # iterating taskseq cannot fail\n            for task in taskseq:\n        ...\n    else:\n        util.debug(\'task handler got sentinel\')\n
Run Code Online (Sandbox Code Playgroud)\n\n

不久前,我看到了这篇博客文章,IMO 总结了它的iter(callable, sentinel)优点while True ... break

\n\n
\n

通常,当我们迭代一个对象或直到某个条件发生时,我们会在第一行中了解循环的范围。例如,当阅读以 for book in books 开头的循环时,我们意识到我们\xe2\x80\x99正在迭代所有书籍。当我们看到一个以 while 而不是 Battery.empty() 开头的循环时,我们意识到循环的范围是只要我们还有电池即可。\n 当我们说 \xe2\x80\x9cDoforever\xe2\x80\ x9d(即,虽然 True),它\xe2\x80\x99s 显然这个范围是一个谎言。因此,它要求我们将这个想法牢记在心,并在代码的其余部分中搜索\xe2\x80\x99 将使我们摆脱困境的语句。我们进入循环的信息较少,因此可读性较差。

\n
\n