เหตุใด Python 3 จึงอนุญาตให้“ 00” เป็นลิเทอรัลสำหรับ 0 แต่ไม่อนุญาตให้“ 01” เป็นลิเทอรัลสำหรับ 1


111

เหตุใด Python 3 จึงอนุญาตให้ "00" เป็นลิเทอรัลสำหรับ 0 แต่ไม่อนุญาตให้ "01" เป็นลิเทอรัลสำหรับ 1 มีเหตุผลที่ดีหรือไม่? ความไม่ลงรอยกันนี้ทำให้ฉันงงงวย (และเรากำลังพูดถึง Python 3 ซึ่งตั้งใจที่จะทำลายความเข้ากันได้แบบย้อนหลังเพื่อให้บรรลุเป้าหมายเช่นความสม่ำเสมอ)

ตัวอย่างเช่น:

>>> from datetime import time
>>> time(16, 00)
datetime.time(16, 0)
>>> time(16, 01)
  File "<stdin>", line 1
    time(16, 01)
              ^
SyntaxError: invalid token
>>>

42
ตอนนี้ไม่สามารถลบออกได้มิฉะนั้นจะทำให้ความเข้ากันได้ย้อนหลังกับคำถามนี้!
John La Rooy

คำตอบ:


103

ต่อhttps://docs.python.org/3/reference/lexical_analysis.html#integer-literals :

ตัวอักษรจำนวนเต็มอธิบายโดยนิยามศัพท์ต่อไปนี้:

integer        ::=  decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::=  nonzerodigit digit* | "0"+
nonzerodigit   ::=  "1"..."9"
digit          ::=  "0"..."9"
octinteger     ::=  "0" ("o" | "O") octdigit+
hexinteger     ::=  "0" ("x" | "X") hexdigit+
bininteger     ::=  "0" ("b" | "B") bindigit+
octdigit       ::=  "0"..."7"
hexdigit       ::=  digit | "a"..."f" | "A"..."F"
bindigit       ::=  "0" | "1"

ไม่มีขีดจำกัดความยาวของตัวอักษรจำนวนเต็มนอกเหนือจากสิ่งที่สามารถเก็บไว้ในหน่วยความจำที่มีอยู่

โปรดทราบว่าไม่อนุญาตให้นำเลขศูนย์นำหน้าในเลขฐานสิบที่ไม่ใช่ศูนย์ สิ่งนี้มีไว้สำหรับการเปลี่ยนรูปแบบด้วยตัวอักษรฐานแปดสไตล์ C ซึ่ง Python ใช้ก่อนเวอร์ชัน 3.0

ดังที่ระบุไว้ที่นี่ไม่อนุญาตให้นำเลขศูนย์นำหน้าในเลขฐานสิบที่ไม่ใช่ศูนย์ "0"+ถูกกฎหมายเป็นกรณีพิเศษซึ่งไม่มีอยู่ใน Python 2 :

integer        ::=  decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::=  nonzerodigit digit* | "0"
octinteger     ::=  "0" ("o" | "O") octdigit+ | "0" octdigit+

SVN กระทำ r55866ใช้ PEP 3127 ในโทเค็นไนเซอร์ซึ่งห้ามใช้0<octal>หมายเลขเก่า อย่างไรก็ตามอยากรู้อยากเห็นมันยังเพิ่มบันทึกนี้:

/* in any case, allow '0' as a literal */

ด้วยnonzeroแฟล็กพิเศษที่พ่นSyntaxErrorหากลำดับของตัวเลขต่อไปนี้มีตัวเลขที่ไม่ใช่ศูนย์เท่านั้น

นี่เป็นเรื่องแปลกเนื่องจากPEP 3127ไม่อนุญาตในกรณีนี้:

PEP นี้เสนอว่าความสามารถในการระบุเลขฐานแปดโดยใช้ศูนย์นำหน้าจะถูกลบออกจากภาษาใน Python 3.0 (และโหมดแสดงตัวอย่าง Python 3.0 ที่ 2.6) และSyntaxError จะเพิ่มขึ้นเมื่อใดก็ตามที่ "0" นำหน้าคือ ตามด้วยตัวเลขอื่นทันที

(เน้นเหมือง)

ดังนั้นความจริงที่ว่ามีการอนุญาตให้ใช้เลขศูนย์หลายตัวจึงเป็นการละเมิด PEP ในทางเทคนิคและโดยพื้นฐานแล้ว Georg Brandl จะนำมาใช้เป็นกรณีพิเศษ เขาได้ทำการเปลี่ยนแปลงเอกสารที่เกี่ยวข้องเพื่อทราบว่า"0"+เป็นกรณีที่ถูกต้องสำหรับdecimalinteger(ก่อนหน้านี้ได้รับการคุ้มครองภายใต้octinteger)

เราคงไม่มีทางรู้แน่ชัดว่าเหตุใด Georg จึงเลือกที่จะทำให้"0"+ถูกต้อง - มันอาจยังคงเป็นกรณีมุมแปลก ๆ ใน Python ตลอดไป


อัปเดต [28 ก.ค. 2558]: คำถามนี้นำไปสู่กระทู้สนทนาที่มีชีวิตชีวาเกี่ยวกับแนวคิดเกี่ยวกับงูหลามที่Georg พูดใน :

Steven D'Aprano เขียนว่า:

ทำไมถึงถูกกำหนดแบบนั้น? [... ] ทำไมเราต้องเขียน 0000 เพื่อให้ได้ศูนย์?

ฉันบอกคุณได้ แต่ฉันต้องฆ่าคุณ

เฟรด

ต่อมาเธรดได้สร้างรายงานข้อบกพร่องนี้โดยมีจุดประสงค์เพื่อกำจัดกรณีพิเศษนี้ ที่นี่เฟรดพูดว่า :

ฉันจำสาเหตุของการเปลี่ยนแปลงโดยเจตนานี้ไม่ได้ (ดังที่เห็นจากการเปลี่ยนแปลงเอกสาร)

ฉันไม่สามารถหาเหตุผลที่ดีสำหรับการเปลี่ยนแปลงนี้ได้ในขณะนี้ [... ]

และด้วยเหตุนี้เราจึงมี: เหตุผลที่ชัดเจนเบื้องหลังความไม่ลงรอยกันนี้จะสูญหายไปตามกาลเวลา

ในที่สุดโปรดทราบว่ารายงานข้อผิดพลาดถูกปฏิเสธ: ศูนย์นำหน้าจะยังคงได้รับการยอมรับเฉพาะในจำนวนเต็มศูนย์สำหรับ Python 3.x ที่เหลือ


6
ทำไมคุณถึงพูดว่า "เราคงไม่มีทางรู้แน่ชัดว่าทำไมเฟรดถึงเลือก ... " ถ้ามีคนรู้จักเขาเห็นกระทู้นี้และแจ้งเรื่องนี้เขาอาจจะมาให้คำตอบ! (เว้นแต่คุณจะรู้ว่าเขาปฏิเสธที่จะพูดคุยเกี่ยวกับงาน Python ในอดีตของเขาตลอดไปหรือสถานการณ์บางอย่างที่คล้ายกัน)
walrus

1
ฉันไม่เข้าใจว่าทำไมพวกเขาถึงไม่ทำ Python 2 octintegercase ตัวที่"0" octdigit*สอง 0เป็นลิเทอรัลฐานแปดใน C / C ++
Random832

1
จริงๆแล้วภาษาอังกฤษค่อนข้างคลุมเครือในเรื่องนี้ คำว่า "อื่น" อาจหมายถึง "อีกอัน" หรืออาจหมายถึง "อีกอัน" ก็ได้ การตีความคำพูดที่เป็นตัวหนาในภาษาอังกฤษที่ถูกต้องจาก PEP 3127 คือการหมายความว่า "SyntaxError จะขึ้นเมื่อใดก็ตามที่นำหน้า '0' ตามด้วยตัวเลขอื่นที่ไม่ใช่ '0' ทันที" ฉันไม่แน่ใจว่านั่นคือสิ่งที่ตั้งใจไว้จริงหรือไม่ ( แม้ว่าการตีความดังกล่าวจะได้รับการสนับสนุนโดยรหัสจริง) แต่ในกรณีใดก็ตามฉันคิดว่าไม่ถูกต้องที่จะบอกว่า PEP ถูกละเมิดทางเทคนิคโดยไม่มีการชี้แจงเพิ่มเติมของประโยคนั้น
GrandOpener

2
@GrandOpener: โปรดทราบว่า001ผิดกฎหมายในขณะที่การตีความของคุณจะทำให้ถูกต้องตามกฎหมาย (เนื่องจากความหมายของ "ทันที" ควรค่อนข้างคลุมเครือ)
nneonneo

จุดดี. ดังนั้น PEP จึงถูกละเมิดอย่างแน่นอน สิ่งที่คลุมเครือคือลักษณะที่แท้จริงที่ถูกละเมิด :)
GrandOpener

17

เป็นกรณีพิเศษ ( "0"+)

2.4.4. ตัวอักษรจำนวนเต็ม

ตัวอักษรจำนวนเต็มอธิบายโดยนิยามศัพท์ต่อไปนี้:

จำนวนเต็ม :: = decimalinteger | ออกทินเทเกอร์ | hexinteger | bininteger
decimalinteger :: = เลขที่ไม่ใช่หลัก * | "0" +
nonzerodigit :: = "1" ... "9"
หลัก :: = "0" ... "9"
octinteger :: = "0" ("o" | "O") octdigit +
hexinteger :: = "0" ("x" | "X") hexdigit +
bininteger :: = "0" ("b" | "B") bindigit +
octdigit :: = "0" ... "7"
เลขฐานสิบหก :: = หลัก | "ก" ... "ฉ" | "ก" ... "ฉ"
bindigit :: = "0" | "1"

ถ้าคุณดูไวยากรณ์มันง่ายที่จะเห็นว่า0ต้องเป็นกรณีพิเศษ ฉันไม่แน่ใจว่าทำไม ' +' ถึงถือว่าจำเป็นที่นั่น ได้เวลาเจาะลึกรายชื่อผู้พัฒนาอีเมล ...


น่าสนใจที่ทราบว่าใน Python2 มี0การแยกวิเคราะห์มากกว่าหนึ่งรายการเป็นoctinteger(ผลลัพธ์สุดท้ายยังคงเป็นอยู่0)

decimalinteger :: = เลขที่ไม่ใช่หลัก * | "0"
octinteger :: = "0" ("o" | "O") octdigit + | octdigit + "0"

1
และความคิดใด ๆ ว่าทำไมจึงมี"0"+และไม่มี"0"?
lejlot

1
@lejlot ยังไม่ได้ - แต่ฉันรู้สึกทึ่ง แน่นอนว่าเป็นส่วนหนึ่งของข้อมูลจำเพาะ
John La Rooy

3

Python2 ใช้เลขศูนย์นำหน้าเพื่อระบุเลขฐานแปด:

>>> 010
8

เพื่อหลีกเลี่ยงนี้พฤติกรรม (? ทำให้เข้าใจผิด) Python3 ต้องใช้คำนำหน้าอย่างชัดเจน0b, 0o, 0x:

>>> 0o10
8

15
คำถามยังคงอยู่: ทำไมถึง00ได้รับอนุญาต? (และ000, 0000ฯลฯ )
ไมเคิลเกียรี่

4
@MichaelGeary: อาจเป็นเพราะมันไม่คลุมเครือ (00000000 เป็น 0 โดยไม่คำนึงถึงฐาน) และการลบออกจะไม่จำเป็นต้องทำลายรหัส? ยังแปลก ๆ .
RemcoGerlich

5
@RemcoGerlich ถ้าฉันไม่ผิด01ก็1ไม่คำนึงถึงฐานด้วย
Holt

2
@Holt: แต่อนุญาต "0" + "1"? เป็นกรณีพิเศษอาจจะยิ่งสับสน
RemcoGerlich

4
@RemcoGerlich ไม่เคยพูดว่าจะไม่;) ฉันแค่บอกว่าcan't be ambiguousมันไม่ใช่ข้อโต้แย้งเพราะ01ไม่สามารถคลุมเครือได้เช่นกัน IMO 00กรณีนี้เป็นเพียงกรณีพิเศษเพราะเป็น0สิ่งที่ไม่ควร
Holt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.