Является ли `iter(callable, sentinel)` все еще Pythonic?

Второй аргумент функции iter полезен для перебора объектов, которые не определяют себя как итерируемые, например двоичных файлов. :

response = b''
for block in iter(partial(f.read, 256), b''):
    response += block

Однако в Python 3.8 у нас теперь есть «оператор моржа», который в Что нового в Python 3.8 упоминается как способ решения указанной выше проблемы:

# Loop over fixed length blocks
while (block := f.read(256)) != '':
    process(block)

Интересно, последнее сейчас считается "правильным подходом"? И если да, то нужен ли когда-либо второй аргумент iter, так как любой код в форме

for x in iter(f, y):
    g(x)

теперь можно было бы написать:

while (x := f()) != y:
    g(x)

Я предполагаю, что все еще могут быть случаи, когда мы не хотим сразу зацикливать итерируемый объект, такой как b''.join(iter(partial(f.read, 256), b'')) или какой-то код (хотя это быстро становится довольно сложным). Также такой цикл, как for i, x in enumerate(iter(f, y)):, может быть сложно перевести в новый синтаксис(?)

PEP для walrus упоминает только 2-arg iter в примере while h(x := f()): g(x), который, как говорится, не может быть тривиально переведен на iter.

Python обычно имеет довольно точные рекомендации по такого рода вещам, но мне не удалось найти их для этой конкретной проблемы. Можете вы помочь мне?


person Thomas Ahle    schedule 26.01.2020    source источник
comment
f.read(256) не является допустимым первым аргументом для двухаргументной формы iter; Я думаю, вы имеете в виду iter(lambda: f.read(256), b'').   -  person chepner    schedule 26.01.2020
comment
Я считаю, что, поскольку это очень новая функция, потребуется некоторое время, чтобы увидеть, какая из них станет доминирующей в этом контексте, и каковы плюсы и минусы каждой из них.   -  person Olivier Melançon    schedule 26.01.2020
comment
iter(..., ...) по-прежнему будет полезен в качестве отправной точки для создания итератора, например, с использованием типов, определенных модулем itertools.   -  person chepner    schedule 26.01.2020
comment
С совершенно другой точки зрения можно утверждать, что iter(callable, sentinel) никогда не был Pythonic, учитывая, как редко его можно увидеть. :)   -  person chepner    schedule 26.01.2020
comment
Как же тогда это стало рекомендацией Transforming Code to Beautiful, Idiomatic Python: P. Я предполагаю, что это было просто навязано, но так и не было принято... Может быть, тогда его просто нужно удалить.   -  person Thomas Ahle    schedule 26.01.2020


Ответы (1)


Выражение присваивания полезно, если вас в первую очередь интересует немедленная итерация по итератору, но оно не поможет вам определить итератор для использования в другом месте.

Например, вы можете захотеть создать итератор, который сначала будет заключен в map, или filter, или itertools.islice, прежде чем окончательно перебирать окончательный результат с помощью цикла for.

person chepner    schedule 26.01.2020
comment
Я полагаю, вы могли бы использовать (x for _ in count() if (x := f()) != y) как моржовую версию iter(f, y). - person Thomas Ahle; 26.01.2020
comment
Хотя я понимаю вашу точку зрения: используйте циклы walrus для циклов и используйте iter для создания итераторов, которые не будут немедленно зацикливаться. - person Thomas Ahle; 26.01.2020
comment
И даже в циклах while я думаю, что := лучше всего использовать для простых присваиваний и проверок. Вероятно, есть код, который лучше всего писать с использованием while True: x = f(x); if <some condition of x>: break; .... - person chepner; 26.01.2020
comment
Даже это все еще немного отличается: этот генератор работает вечно, а не останавливается, когда x == y. - person chepner; 26.01.2020
comment
Хорошая точка зрения. Я думаю, что случай отсутствия итерации ясен тогда. И вы правы, в случае с циклами while было уже два способа сделать это. - person Thomas Ahle; 26.01.2020