while (1) so với while (True) - Tại sao có sự khác biệt (trong Python 2 bytecode)?


115

Bị thu hút bởi câu hỏi này về các vòng lặp vô hạn trong perl: while (1) Vs. for (;;) Có sự khác biệt về tốc độ không? , Tôi quyết định chạy một phép so sánh tương tự trong python. Tôi mong đợi rằng trình biên dịch sẽ tạo ra cùng một mã byte cho while(True): passwhile(1): pass, nhưng đây thực sự không phải là trường hợp trong python2.7.

Tập lệnh sau:

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)

tạo ra các kết quả sau:

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        

Sử dụng while Truephức tạp hơn đáng kể. Tại sao thế này?

Trong các ngữ cảnh khác, python hoạt động như thể Truebằng 1:

>>> True == 1
True

>>> True + True
2

Tại sao whilephân biệt hai?

Tôi nhận thấy rằng python3 không đánh giá các câu lệnh bằng cách sử dụng các hoạt động giống hệt nhau:

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ó sự thay đổi nào trong python3 đối với cách đánh giá boolean không?


Câu trả lời:


124

Trong Python 2.x, Truekhông phải là một từ khóa, mà chỉ là một hằng số toàn cầu tích hợp sẵn được định nghĩa thành 1 trong boolkiểu. Do đó, trình thông dịch vẫn phải tải nội dung của True. Nói cách khác, Truecó thể chuyển nhượng lại:

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

Trong Python 3.x, nó thực sự trở thành một từ khóa và một hằng số thực:

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

do đó trình thông dịch có thể thay thế while True:vòng lặp bằng một vòng lặp vô hạn.


1
@MH: AFAIK, thật là khẩn trương để chuyển từ khóa sang ngôn ngữ.
S.Lott


Điều này có nghĩa while 1while Truegiống hệt nhau trong Python 3 không?
Stevoisiak

@ StevenM.Vascellaro Có.
kennytm

14

Điều này không hoàn toàn đúng,

do đó trình thông dịch có thể thay thế vòng lặp while True: bằng một vòng lặp vô hạn.

vì người ta vẫn có thể thoát ra khỏi vòng lặp. Nhưng đúng là elsemệnh đề của vòng lặp như vậy sẽ không bao giờ được truy cập trong Python 3. Và cũng đúng là việc đơn giản hóa tra cứu giá trị khiến nó chạy nhanh như while 1trong Python 2.

So sánh hiệu suất

Thể hiện sự khác biệt về thời gian đối với vòng lặp while có phần không đáng kể:

Thiết lập

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

Giải trình

Để giải thích sự khác biệt, trong Python 2:

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

nhưng trong Python 3:

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

Truelà một từ khóa trong Python 3, trình thông dịch không cần phải tra cứu giá trị để xem liệu ai đó đã thay thế nó bằng một số giá trị khác hay không. Nhưng vì một Truegiá trị có thể gán cho một giá trị khác, nên trình thông dịch phải tra cứu nó mọi lúc.

Kết luận cho Python 2

Nếu bạn có một vòng lặp dài và chặt chẽ trong Python 2, bạn có thể nên sử dụng while 1:thay thế while True:.

Kết luận cho Python 3

Sử dụng while True:nếu bạn không có điều kiện thoát ra khỏi vòng lặp của mình.


3

Đây là một câu hỏi 7 tuổi đã có câu trả lời tuyệt vời, nhưng một quan niệm sai lầm trong câu hỏi, không được đề cập trong bất kỳ câu trả lời nào, khiến nó có khả năng gây nhầm lẫn cho một số câu hỏi khác được đánh dấu là trùng lặp.

Trong các ngữ cảnh khác, python hoạt động như thể True bằng 1:

>>> True == 1
True

>>> True + True
2

Tại sao trong khi phân biệt hai?

Trên thực tế, whilekhông có gì khác biệt ở đây cả. Nó phân biệt 1Truegiống hệt như cách mà +ví dụ làm.


Đây là 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

Bây giờ hãy so sánh:

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

Nó tạo ra một LOAD_GLOBAL (True)cho mỗi cái Truevà không có gì trình tối ưu hóa có thể làm với một cái toàn cầu. Vì vậy, hãy whilephân biệt 1Truevì lý do chính xác mà +làm. (Và ==không phân biệt chúng bởi vì trình tối ưu hóa không tối ưu hóa các so sánh.)


Bây giờ hãy so sánh 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

Ở đây, nó phát ra một LOAD_CONST (True)cho từ khóa, mà trình tối ưu hóa có thể tận dụng. Vì vậy, True + 1 không phân biệt, vì lý do chính xác while Truethì không. (Và ==vẫn không phân biệt được chúng vì trình tối ưu hóa không tối ưu hóa các so sánh.)


Trong khi đó, nếu mã không được tối ưu hóa, trình thông dịch sẽ xử lý True1hoàn toàn giống nhau trong cả ba trường hợp này. boollà một lớp con của intvà kế thừa hầu hết các phương thức của nó intTruecó giá trị số nguyên bên trong là 1. Vì vậy, cho dù bạn đang thực hiện whilekiểm tra ( __bool__trong 3.x, __nonzero__trong 2.x), so sánh ( __eq__) hay số học ( __add__), bạn đang gọi cùng một phương thức cho dù bạn sử dụng Truehoặc 1.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.