while (1) vs. while (True) - Perché c'è una differenza (in bytecode python 2)?


115

Incuriosito da questa domanda sui loop infiniti in perl: while (1) Vs. per (;;) C'è una differenza di velocità? , Ho deciso di eseguire un confronto simile in Python. Mi aspettavo che il compilatore generasse lo stesso byte code per while(True): passe while(1): pass, ma in realtà non è così in python2.7.

Il seguente script:

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)

produce i seguenti risultati:

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        

L'utilizzo while Trueè notevolmente più complicato. Perchè è questo?

In altri contesti, python si comporta come se fosse Trueuguale a 1:

>>> True == 1
True

>>> True + True
2

Perché whiledistingue i due?

Ho notato che python3 valuta le istruzioni utilizzando operazioni identiche:

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         

C'è un cambiamento in python3 nel modo in cui vengono valutati i booleani?


Risposte:


124

In Python 2.x, Truenon è una parola chiave, ma solo una costante globale incorporata definita a 1 nel booltipo. Pertanto l'interprete deve ancora caricare il contenuto di True. In altre parole, Trueè riassegnabile:

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

In Python 3.x diventa davvero una parola chiave e una vera costante:

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

così l'interprete può sostituire il while True:ciclo con un ciclo infinito.


1
@ MH: AFAIK, è stato un espediente per inserire la parola chiave nella lingua.
S.Lott


Questo significa while 1e while Truesono identici in Python 3?
Stevoisiak

@ StevenM.Vascellaro Sì.
kennytm

14

Questo non è del tutto corretto

così l'interprete può sostituire il ciclo while True: con un ciclo infinito.

come si può ancora uscire dal ciclo. Ma è vero che la elseclausola di un ciclo del genere non sarebbe mai accessibile in Python 3. Ed è anche vero che la semplificazione della ricerca del valore la fa funzionare altrettanto velocemente come while 1in Python 2.

Confronto delle prestazioni

Dimostrare la differenza di tempo per un ciclo while un po 'non banale:

Impostare

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

Spiegazione

Per spiegare la differenza, in Python 2:

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

ma in Python 3:

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

Poiché Trueè una parola chiave in Python 3, l'interprete non deve cercare il valore per vedere se qualcuno lo ha sostituito con un altro valore. Ma poiché si può assegnare Trueun altro valore, l'interprete deve cercarlo ogni volta.

Conclusione per Python 2

Se hai un ciclo stretto e di lunga durata in Python 2, probabilmente dovresti usare while 1:invece di while True:.

Conclusione per Python 3

Usalo while True:se non hai le condizioni per uscire dal tuo loop.


3

Questa è una domanda di 7 anni che ha già un'ottima risposta, ma un'idea sbagliata nella domanda, che non viene affrontata in nessuna delle risposte, la rende potenzialmente confusa per alcune delle altre domande contrassegnate come duplicate.

In altri contesti, python si comporta come se True fosse uguale a 1:

>>> True == 1
True

>>> True + True
2

Perché mentre distingue i due?

In effetti, whilenon sta facendo niente di diverso qui. Si distingue 1e Truenello stesso modo in cui lo +fa l' esempio.


Ecco 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

Ora confronta:

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

Emette un LOAD_GLOBAL (True)per ciascuno Truee non c'è niente che l'ottimizzatore possa fare con un globale. Quindi, whiledistingue 1e Trueper lo stesso motivo per cui lo +fa. (E ==non li distingue perché l'ottimizzatore non ottimizza i confronti.)


Ora confronta 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

Qui emette un LOAD_CONST (True)per la parola chiave, di cui l'ottimizzatore può trarre vantaggio. Quindi, True + 1 non fa distinzione, esattamente per la stessa ragione while True. (E ==ancora non li distingue perché l'ottimizzatore non ottimizza i confronti.)


Nel frattempo, se il codice non è ottimizzato, l'interprete finisce per trattare Trueed 1esattamente lo stesso in tutti e tre questi casi. boolè una sottoclasse di inted eredita la maggior parte dei suoi metodi da inte Trueha un valore intero interno di 1. Quindi, sia che tu stia eseguendo un whiletest ( __bool__in 3.x, __nonzero__in 2.x), un confronto ( __eq__) o un'aritmetica ( __add__), stai chiamando lo stesso metodo sia che usi Trueo 1.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.