while (1) เทียบกับ while (True) - เหตุใดจึงมีความแตกต่าง (ใน python 2 bytecode)


115

ทึ่งกับคำถามนี้เกี่ยวกับลูปที่ไม่มีที่สิ้นสุดใน perl: while (1) Vs. สำหรับ (;;) ความเร็วต่างกันไหม ฉันตัดสินใจที่จะทำการเปรียบเทียบที่คล้ายกันใน 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มีความซับซ้อนมากขึ้นอย่างเห็นได้ชัด ทำไมถึงเป็นแบบนี้?

ในบริบทอื่น ๆ python ทำหน้าที่ราวกับว่า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 เป็นการสมควรที่จะรับคีย์เวิร์ดเป็นภาษา
ล็อต

2
ดูdocs.python.org/whatsnew/2.3.html#pep-285-a-boolean-typeและpython.org/dev/peps/pep-0285สำหรับประวัติบางส่วน
เน็ดดีลี่

นี่หมายความว่าwhile 1และwhile Trueเหมือนกันใน Python 3 หรือไม่?
Stevoisiak

@ StevenM.Vascellaro ใช่
kennytm

14

นี่ไม่ถูกต้องนัก

ดังนั้นล่ามจึงสามารถแทนที่ while True: loop ด้วยลูปที่ไม่มีที่สิ้นสุด

เพราะยังสามารถหลุดออกจากวงได้ แต่เป็นความจริงที่ว่า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

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

คำอธิบาย

เพื่ออธิบายความแตกต่างใน 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และไม่มีอะไรที่เครื่องมือเพิ่มประสิทธิภาพสามารถทำได้กับ global ดังนั้น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.x __nonzero__ใน 2.x) การเปรียบเทียบ ( __eq__) หรือเลขคณิต ( __add__) คุณกำลังเรียกใช้วิธีการเดียวกันไม่ว่าคุณจะใช้Trueหรือ1.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.