while (1) vs. while (True) - Почему существует разница (в байт-коде python 2)?

115

Заинтриговал этот вопрос о бесконечных циклах в perl: while (1) Vs. for (;;) Есть ли разница в скорости? , Я решил провести подобное сравнение на python. Я ожидал, что компилятор сгенерирует тот же байт-код для while(True): passи while(1): pass, но на самом деле это не так в python2.7.

Следующий сценарий:

import dis

def while_one():
    while 1:
        pass

def while_true():
    while True:
        pass

print("while 1")
print("----------------------------")
dis.dis(while_one)

print("while True")
print("----------------------------")
dis.dis(while_true)

дает следующие результаты:

while 1
----------------------------
  4           0 SETUP_LOOP               3 (to 6)

  5     >>    3 JUMP_ABSOLUTE            3
        >>    6 LOAD_CONST               0 (None)
              9 RETURN_VALUE        
while True
----------------------------
  8           0 SETUP_LOOP              12 (to 15)
        >>    3 LOAD_GLOBAL              0 (True)
              6 JUMP_IF_FALSE            4 (to 13)
              9 POP_TOP             

  9          10 JUMP_ABSOLUTE            3
        >>   13 POP_TOP             
             14 POP_BLOCK           
        >>   15 LOAD_CONST               0 (None)
             18 RETURN_VALUE        

Использование while Trueзаметно сложнее. Почему это?

В других контекстах python действует так, как если бы он Trueравен 1:

>>> True == 1
True

>>> True + True
2

Почему whileразличают два?

Я заметил, что python3 оценивает операторы с использованием идентичных операций:

while 1
----------------------------
  4           0 SETUP_LOOP               3 (to 6) 

  5     >>    3 JUMP_ABSOLUTE            3 
        >>    6 LOAD_CONST               0 (None) 
              9 RETURN_VALUE         
while True
----------------------------
  8           0 SETUP_LOOP               3 (to 6) 

  9     >>    3 JUMP_ABSOLUTE            3 
        >>    6 LOAD_CONST               0 (None) 
              9 RETURN_VALUE         

Есть ли в python3 изменения в способе оценки логических значений?

AndrewF
источник
Возможный дубликат. В чем разница между «while 1» и «while True»?
Martey

Ответы:

125

В Python 2.x Trueэто не ключевое слово, а просто встроенная глобальная константа , для которой boolтип определен как 1 . Следовательно, интерпретатору по-прежнему необходимо загружать содержимое True. Другими словами, Trueможно переназначить:

Python 2.7 (r27:82508, Jul  3 2010, 21:12:11) 
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 4
>>> True
4

В Python 3.x это действительно становится ключевым словом и настоящей константой:

Python 3.1.2 (r312:79147, Jul 19 2010, 21:03:37) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> True = 4
  File "<stdin>", line 1
SyntaxError: assignment to keyword

таким образом интерпретатор может заменить while True:цикл бесконечным циклом.

kennytm
источник
1
@MH: AFAIK, было уместно ввести ключевое слово в язык.
S.Lott
Означает ли это, while 1и while Trueони идентичны в Python 3?
Stevoisiak
@ Стивен Васселларо Да.
kennytm
14

Это не совсем так,

таким образом, интерпретатор может заменить цикл while True: бесконечным циклом.

поскольку еще можно вырваться из петли. Но верно, что такое предложение цикла elseникогда не будет доступно в Python 3. И также верно, что упрощение поиска значения заставляет его работать так же быстро, как while 1в Python 2.

Сравнение производительности

Демонстрация разницы во времени для несколько нетривиального цикла while:

Настроить

def while1():
    x = 0
    while 1:
        x += 1
        if x == 10:
            break

def whileTrue():
    x = 0
    while True:
        x += 1
        if x == 10:
            break

Python 2

>>> import timeit
>>> min(timeit.repeat(while1))
0.49712109565734863
>>> min(timeit.repeat(whileTrue))
0.756627082824707

Python 3

>>> import timeit
>>> min(timeit.repeat(while1))
0.6462970309949014
>>> min(timeit.repeat(whileTrue))
0.6450748789939098

объяснение

Чтобы объяснить разницу в Python 2:

>>> import keyword
>>> 'True' in keyword.kwlist
False

но в Python 3:

>>> import keyword
>>> 'True' in keyword.kwlist
True
>>> True = 'true?'
  File "<stdin>", line 1
SyntaxError: can't assign to keyword

Поскольку Trueэто ключевое слово в Python 3, интерпретатору не нужно искать значение, чтобы увидеть, заменил ли его кто-то другим значением. Но поскольку одно значение можно присвоить Trueдругому, интерпретатору приходится каждый раз его искать.

Заключение для Python 2

Если у вас в Python 2 сложный и продолжительный цикл, вам, вероятно, следует использовать while 1:вместо while True:.

Заключение для Python 3

Используйте, while True:если у вас нет условий для выхода из цикла.

Аарон Холл
источник
3

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

В других контекстах python действует так, как будто True равно 1:

>>> True == 1
True

>>> True + True
2

Почему while различает эти два?

На самом деле whileздесь вообще ничего не делается. Это отличает 1и Trueточно таким же образом , что +пример делает.


Вот 2.7:

>>> dis.dis('True == 1')
  1           0 LOAD_GLOBAL              0 (True)
              3 LOAD_CONST               1 (1)
              6 COMPARE_OP               2 (==)
              9 RETURN_VALUE

>>> dis.dis('True == 1')
  1           0 LOAD_GLOBAL              0 (True)
              3 LOAD_GLOBAL              0 (True)
              6 BINARY_ADD
              9 RETURN_VALUE

Теперь сравните:

>>> dis.dis('1 + 1')
  1           0 LOAD_CONST               1 (2)
              3 RETURN_VALUE

Он генерирует a LOAD_GLOBAL (True)для каждого True, и оптимизатор ничего не может сделать с global. Итак, whileотличает 1и Trueпо той же причине, что +и. (И ==не различает их, потому что оптимизатор не оптимизирует сравнения.)


Теперь сравните 3.6:

>>> dis.dis('True == 1')
  1           0 LOAD_CONST               0 (True)
              2 LOAD_CONST               1 (1)
              4 COMPARE_OP               2 (==)
              6 RETURN_VALUE

>>> dis.dis('True + True')
  1           0 LOAD_CONST               1 (2)
              2 RETURN_VALUE

Здесь он генерирует LOAD_CONST (True)для ключевого слова, чем может воспользоваться оптимизатор . Итак, True + 1 не различает по той же причине while True. (И ==все равно не различает их, потому что оптимизатор не оптимизирует сравнения.)


В том же время, если код не оптимизированные, интерпретатор заканчивает обработку Trueи 1точно так же во всех этих трех случаях. boolявляется подклассом intи наследует большинство его методов от него intи Trueимеет внутреннее целочисленное значение 1. Итак, выполняете ли вы whileтест ( __bool__в 3.x, __nonzero__в 2.x), сравнение ( __eq__) или арифметические операции. ( __add__), вы вызываете один и тот же метод независимо от того, используете ли вы Trueили 1.

abarnert
источник