Я видел два способа создания бесконечного цикла в Python:
-
while 1: do_something()
-
while True: do_something()
Есть ли разница между ними? Является ли еще один пифонический, чем другой?
Я видел два способа создания бесконечного цикла в Python:
while 1:
do_something()
while True:
do_something()
Есть ли разница между ними? Является ли еще один пифонический, чем другой?
В сущности, это не имеет значения, такие мелочи на самом деле не влияют, является ли что-то "пифоническим" или нет.
Если вы заинтересованы в мелочах, есть некоторые отличия.
встроенный булевский тип не существовал до Python 2.3, поэтому код, предназначенный для запуска в древних версиях, имеет тенденцию использовать while 1:
. Вы увидите это в стандартной библиотеке, например.
Истинные и ложные встроенные функции не зарезервированные слова до Python 3, поэтому их можно присвоить, изменив их значение. Это помогает в случае выше, потому что код может сделать True = 1
для обратной совместимости, но означает, что имя True
нужно искать в словаре глобалов каждый раз, когда он используется.
Из-за вышеуказанного ограничения байт-код, скомпилированный двумя версиями, отличается в Python 2, так как существует оптимизация для постоянных целых чисел, которую он не может использовать для True
. Поскольку Python может компилировать 1
, что он всегда отличен от нуля, он удаляет условный переход и не загружает константу вообще:
>>> import dis
>>> def while_1():
... while 1:
... pass
...
>>> def while_true():
... while True:
... pass
...
>>> dis.dis(while_1)
2 0 SETUP_LOOP 5 (to 8)
3 >> 3 JUMP_ABSOLUTE 3
6 POP_TOP
7 POP_BLOCK
>> 8 LOAD_CONST 0 (None)
11 RETURN_VALUE
>>> dis.dis(while_true)
2 0 SETUP_LOOP 12 (to 15)
>> 3 LOAD_GLOBAL 0 (True)
6 JUMP_IF_FALSE 4 (to 13)
9 POP_TOP
3 10 JUMP_ABSOLUTE 3
>> 13 POP_TOP
14 POP_BLOCK
>> 15 LOAD_CONST 0 (None)
18 RETURN_VALUE
Итак, while True:
немного легче читать, а while 1:
немного добрее к старым версиям Python. Поскольку вам вряд ли понадобится запускать Python 2.2 в эти дни, или вам нужно беспокоиться о подсчете байт-кодов ваших циклов, первое предпочтительнее.
Самый пифонический путь всегда будет самым читаемым. Используйте while True:
Это не имеет большого значения. Также трудно прочитать или понять, хотя лично я всегда использовал while True
, который немного более явный.
В более общем плане, целая серия петель, которые люди пишут на Python, может быть чем-то другим. Иногда я вижу, что люди пишут i = 0; while True: i += 1 ...
, которые могут быть заменены на for i in itertools.count()
и люди, пишущие while True: foo = fun() if foo is None: break
, когда это можно записать for foo in iter(fun, None)
, что требует обучения, но имеет меньше шаблонов и возможность для глупых ошибок.
Ни.
Оба из них означают, что я должен отсканировать код, ищущий break
, вместо того, чтобы видеть условие остановки в том месте, где он принадлежит.
Я стараюсь избегать такого рода вещей, где это возможно, и если это невозможно, пусть код говорит сам за себя:
while not found_answer:
check_number += 1
if check_number == 42:
found_answer = True
Изменить: Кажется, что слово "избегать" выше было недостаточно ясным. Используя в основном бесконечный цикл и оставляя его где-то внутри цикла (используя break
) обычно следует избегать вообще. Иногда это невозможно. В этом случае мне нравится использовать что-то вроде кода выше, который, однако, по-прежнему представляет собой ту же концепцию – приведенный выше код - не что иное, как компромисс – но, по крайней мере, я могу показать цель цикла в начале – так же, как я бы не назвал функцию do_something_with_args(*args)
.
Я думаю, что это в основном вопрос стиля. Оба должны быть легко понятны как бесконечный цикл.
Однако лично я предпочитаю второй вариант. Это потому, что это просто требует умственного микрошага для понимания, особенно для программистов без фона C.
Первый будет работать и в ранних версиях, где True
еще не определен.
IMO второй вариант более очевиден.
Если бы вы могли избавиться от while
и написать более компактный код, это может быть больше pythonic.
Например:
# Get the even numbers in the range 1..10
# Version 1
l = []
n = 1
while 1:
if n % 2 == 0: l.append(n)
n += 1
if n > 10: break
print l
# Version 2
print [i for i in range(1, 11) if i % 2 == 0]
# Version 3
print range(2, 11, 2)
Если у вас есть алгоритм, который предполагается завершить за конечное время, я бы рекомендовал это, что всегда безопаснее, чем while True
:
maxiter = 1000
for i in xrange(maxiter):
# your code
# on success:
break
else:
# that algorithm has not finished in maxiter steps! do something accordingly
Я считаю, что второе выражение более явное, и, следовательно, более pythonic.
Это только вопрос стиля, любой начинающий программист поймет любой вариант.
Но второй вариант будет работать, только если True
не был назначен False
, что было возможно до Python 3:
>>> True = False
>>> True
False
Лучшим способом является "while True" с условным выходом из цикла.