python - proyectos - mientras(1) vs. por cierto(Verdadero)-¿Por qué hay una diferencia?
keyerror ur_dir (3)
Intrigado por esta pregunta sobre bucles infinitos en perl: mientras (1) vs. para (;;) ¿Hay una diferencia de velocidad? , Decidí ejecutar una comparación similar en Python. Esperaba que el compilador generaría el mismo código de bytes para while(True): pass
y while(1): pass
, pero este no es el caso en python2.7.
El siguiente 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 los siguientes resultados:
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
Usar while True
es notablemente más complicado. ¿Por qué es esto?
En otros contextos, Python actúa como si True
es igual a 1:
>>> True == 1
True
>>> True + True
2
¿Por qué while
distingue a los dos?
Noté que python3 no evalúa las declaraciones usando operaciones idénticas:
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
¿Hay algún cambio en python3 en la forma en que se evalúan los booleanos?
En Python 2.x, True
no es una palabra clave, sino solo una constante global incorporada que se define a 1 en el tipo bool
. Por lo tanto, el intérprete todavía tiene que cargar los contenidos de True
. En otras palabras, True
es reasignable:
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
En Python 3.x realmente se convierte en una palabra clave y una constante real:
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
así el intérprete puede reemplazar el while True:
loop con un ciclo infinito.
Esta es una pregunta de hace 7 años que ya tiene una gran respuesta, pero una idea errónea en la pregunta, que no se aborda en ninguna de las respuestas, la hace potencialmente confusa para algunas de las otras preguntas marcadas como duplicadas.
En otros contextos, Python actúa como si True es igual a 1:
>>> True == 1
True
>>> True + True
2
¿Por qué mientras distingue a los dos?
De hecho, while
no está haciendo nada diferente aquí en absoluto. Distingue 1
y True
exactamente de la misma manera que el ejemplo +
.
Aquí está 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
Ahora compara:
>>> dis.dis(''1 + 1'')
1 0 LOAD_CONST 1 (2)
3 RETURN_VALUE
Está emitiendo un LOAD_GLOBAL (True)
para cada True
, y no hay nada que el optimizador pueda hacer con un global. Entonces, while
distingue 1
y True
por la misma razón que +
hace. (Y ==
no los distingue porque el optimizador no optimiza las comparaciones).
Ahora compare 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
Aquí, emite un LOAD_CONST (True)
para la palabra clave, que el optimizador puede aprovechar. Entonces, True + 1
no distingue, por exactamente la misma razón, while True
no lo hace. (Y ==
aún no los distingue porque el optimizador no optimiza las comparaciones).
Mientras tanto, si el código no se optimiza, el intérprete termina tratando True
y 1
exactamente igual en los tres casos. bool
es una subclase de int
, y hereda la mayoría de sus métodos de int
, y True
tiene un valor entero interno de 1. Entonces, si estás haciendo una prueba __bool__
( __bool__
en 3.x, __nonzero__
en 2.x), una comparación ( __eq__
) o aritmética ( __add__
), está llamando al mismo método si usa True
o 1
.
Esto no está del todo bien,
así el intérprete puede reemplazar el tiempo True: loop con un ciclo infinito.
como uno todavía puede salir del circuito. Pero es cierto que nunca se accederá a la cláusula else
ese bucle en Python 3. Y también es cierto que al simplificar la búsqueda de valor, se ejecuta tan rápido como while 1
en Python 2.
Comparación de rendimiento
Demostrando la diferencia en el tiempo para un ciclo while algo trivial:
Preparar
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
Explicación
Para explicar la diferencia, en Python 2:
>>> import keyword
>>> ''True'' in keyword.kwlist
False
pero en Python 3:
>>> import keyword
>>> ''True'' in keyword.kwlist
True
>>> True = ''true?''
File "<stdin>", line 1
SyntaxError: can''t assign to keyword
Como True
es una palabra clave en Python 3, el intérprete no tiene que buscar el valor para ver si alguien lo reemplazó con algún otro valor. Pero como uno puede asignar True
a otro valor, el intérprete debe buscarlo todo el tiempo.
Conclusión para Python 2
Si tiene un ciclo estrecho y prolongado en Python 2, probablemente debería usar while 1:
lugar de while True:
.
Conclusión para Python 3
Use while True:
si no tiene ninguna condición para salir de su loop.