while (1) vs. while (True) - Mengapa ada perbedaan (dalam bytecode python 2)?

115

Penasaran dengan pertanyaan tentang loop tak terbatas dalam perl: while (1) Vs. untuk (;;) Apakah ada perbedaan kecepatan? , Saya memutuskan untuk menjalankan perbandingan serupa dengan python. Saya berharap bahwa kompiler akan menghasilkan kode byte yang sama untuk while(True): passdan while(1): pass, tetapi ini sebenarnya tidak terjadi di python2.7.

Script berikut ini:

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)

menghasilkan hasil sebagai berikut:

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        

Menggunakan while Trueterasa lebih rumit. Kenapa ini?

Dalam konteks lain, python bertindak seolah-olah Truesama dengan 1:

>>> True == 1
True

>>> True + True
2

Mengapa whilemembedakan keduanya?

Saya perhatikan bahwa python3 mengevaluasi pernyataan menggunakan operasi yang identik:

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         

Apakah ada perubahan pada python3 dalam cara mengevaluasi boolean?

AndrewF
sumber
Kemungkinan duplikat dari Apa perbedaan antara "while 1" dan "while True"?
Martey

Jawaban:

124

Dalam Python 2.x, Trueini bukan kata kunci, tetapi hanya konstanta global bawaan yang didefinisikan ke 1 dalam booltipe. Oleh karena itu penerjemah masih harus memuat konten True. Dengan kata lain, Truedapat ditetapkan ulang:

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

Dalam Python 3.x ini benar-benar menjadi kata kunci dan konstanta nyata:

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

dengan demikian penerjemah dapat mengganti while True:loop dengan loop tak terbatas.

kennytm
sumber
1
@MH: AFAIK, itu bijaksana untuk memasukkan kata kunci ke dalam bahasa.
S. Lott
Apakah ini berarti while 1dan while Trueidentik dengan Python 3?
Stevoisiak
@ Steven.ascellaro Ya.
kennytm
14

Ini kurang tepat,

dengan demikian interpreter dapat mengganti while True: loop dengan loop tak terhingga.

karena seseorang masih bisa keluar dari lingkaran. Tetapi memang benar bahwa elseklausa loop seperti itu tidak akan pernah diakses dengan Python 3. Dan juga benar bahwa menyederhanakan pencarian nilai membuatnya berjalan secepat while 1di Python 2.

Perbandingan Kinerja

Mendemonstrasikan perbedaan waktu untuk loop sementara yang tidak sepele:

Mempersiapkan

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

Penjelasan

Untuk menjelaskan perbedaannya, dengan Python 2:

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

tetapi dengan Python 3:

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

Karena Truemerupakan kata kunci dalam Python 3, penerjemah tidak perlu mencari nilainya untuk melihat apakah seseorang menggantinya dengan nilai lain. Tetapi karena seseorang dapat menetapkan Truenilai lain, penerjemah harus mencarinya setiap saat.

Kesimpulan untuk Python 2

Jika Anda memiliki ketat, lingkaran lama berjalan di Python 2, Anda mungkin harus menggunakan while 1:bukan while True:.

Kesimpulan untuk Python 3

Gunakan while True:jika Anda tidak memiliki kondisi untuk keluar dari loop Anda.

Aaron Hall
sumber
3

Ini adalah pertanyaan 7 tahun yang sudah memiliki jawaban yang bagus, tetapi kesalahpahaman dalam pertanyaan, yang tidak dibahas dalam jawaban mana pun, membuatnya berpotensi membingungkan untuk beberapa pertanyaan lain yang ditandai sebagai duplikat.

Dalam konteks lain, python bertindak seolah-olah True sama dengan 1:

>>> True == 1
True

>>> True + True
2

Mengapa sambil membedakan keduanya?

Faktanya, whiletidak melakukan sesuatu yang berbeda di sini sama sekali. Ini membedakan 1dan Truedengan cara yang persis sama seperti +contoh itu.


Berikut 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

Sekarang bandingkan:

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

Itu memancarkan a LOAD_GLOBAL (True)untuk masing-masing True, dan tidak ada yang dapat dilakukan pengoptimal dengan global. Jadi, whilebedakan 1dan Trueuntuk alasan yang sama persis +. (Dan ==tidak membedakannya karena pengoptimal tidak mengoptimalkan perbandingan.)


Sekarang bandingkan 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

Di sini, ini memancarkan LOAD_CONST (True)untuk kata kunci, yang dapat dimanfaatkan oleh pengoptimal . Jadi, True + 1 tidak membedakan, untuk alasan yang persis sama while Truetidak. (Dan ==masih tidak membedakannya karena pengoptimal tidak mengoptimalkan perbandingan.)


Sementara itu, jika kode tidak dioptimalkan, penerjemah akhirnya memperlakukan Truedan 1persis sama di ketiga kasus ini. booladalah subkelas dari int, dan mewarisi sebagian besar metodenya dari int, dan Truememiliki nilai bilangan bulat internal 1. Jadi, baik Anda melakukan whiletes ( __bool__dalam 3.x, __nonzero__dalam 2.x), perbandingan ( __eq__), atau aritmatika ( __add__), Anda memanggil metode yang sama apakah Anda menggunakan Trueatau 1.

abarnert
sumber