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помітно складніше. Чому це?

В інших контекстах пітон діє так, ніби 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 в тому, як оцінюються булеві?


Відповіді:


124

У Python 2.x - Trueце не ключове слово, а просто вбудована глобальна константа, яка визначена 1 у boolтипі. Тому перекладачеві ще належить завантажувати вміст 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:цикл нескінченним циклом.


1
@MH: AFAIK, ключове слово було доцільним ввести в мову.
S.Lott


Чи означає це while 1і while Trueчи однакові в Python 3?
Stevoisiak

@ StevenM.Vascellaro Так.
kennytm

14

Це не зовсім правильно,

таким чином інтерпретатор може замінити цикл у той час як True: цикл нескінченним.

як ще можна вирватися з петлі. Але це правда, що до цього elseпункту циклу ніколи не можна отримати доступ до Python 3. І правда також, що спрощення пошуку значень змушує його працювати так само швидко, як і while 1в Python 2.

Порівняння продуктивності

Демонструючи різницю у часі для дещо нетривіального циклу:

Налаштування

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

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

Пітон 2

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

Пітон 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

Це 7-річне запитання, на яке вже є чудова відповідь, але неправильне уявлення у питанні, яке не вирішено в жодній із відповідей, робить його потенційним заплутаним для деяких інших питань, позначених як дублікати.

В інших контекстах python діє так, ніби True дорівнює 1:

>>> True == 1
True

>>> True + True
2

Чому поки розрізняють ці два?

Насправді 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

Це випромінює LOAD_GLOBAL (True)для кожного True, і оптимізатор не може зробити з глобальним. Отже, 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.х, __nonzero__2.х), порівняння ( __eq__) чи арифметику ( __add__), ви називаєте той самий метод, чи ви користуєтесь, Trueчи 1.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.