Tại sao Python 3 cho phép “00” là một ký tự cho 0 nhưng không cho phép “01” là một ký tự cho 1?


111

Tại sao Python 3 cho phép "00" là một ký tự cho 0 nhưng không cho phép "01" là một ký tự cho 1? Có một lý do chính đáng? Sự mâu thuẫn này khiến tôi bối rối. (Và chúng ta đang nói về Python 3, thứ đã cố tình phá vỡ khả năng tương thích ngược để đạt được các mục tiêu như tính nhất quán.)

Ví dụ:

>>> 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
Không thể xóa nó ngay bây giờ, nếu không nó sẽ phá vỡ khả năng tương thích ngược với câu hỏi này!
John La Rooy,

Câu trả lời:


103

Theo https://docs.python.org/3/reference/lexical_analysis.html#integer-literals :

Các ký tự số nguyên được mô tả bằng các định nghĩa từ vựng sau:

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"

Không có giới hạn cho độ dài của các ký tự nguyên ngoài những gì có thể được lưu trữ trong bộ nhớ khả dụng.

Lưu ý rằng không cho phép các số 0 ở đầu trong một số thập phân khác 0. Điều này là để phân biệt với các ký tự bát phân kiểu C, mà Python đã sử dụng trước phiên bản 3.0.

Như đã lưu ý ở đây, không cho phép các số 0 đứng đầu trong một số thập phân khác 0 . "0"+là hợp pháp như một trường hợp rất đặc biệt, không có trong Python 2 :

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

SVN cam kết r55866 đã thực hiện PEP 3127 trong trình mã hóa, cấm các 0<octal>số cũ . Tuy nhiên, thật kỳ lạ, nó cũng thêm ghi chú này:

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

với nonzerocờ đặc biệt chỉ ném a SyntaxErrornếu dãy chữ số sau chứa chữ số khác không.

Điều này thật kỳ lạ vì PEP 3127 không cho phép trường hợp này:

PEP này đề xuất rằng khả năng chỉ định một số bát phân bằng cách sử dụng số 0 đứng đầu sẽ bị xóa khỏi ngôn ngữ trong Python 3.0 (và chế độ xem trước Python 3.0 là 2.6) và rằng Lỗi cú pháp sẽ được nâng lên bất cứ khi nào số "0" đứng đầu là ngay sau đó là một chữ số khác .

(nhấn mạnh của tôi)

Vì vậy, thực tế là cho phép nhiều số không về mặt kỹ thuật là vi phạm PEP và về cơ bản đã được Georg Brandl thực hiện như một trường hợp đặc biệt. Anh ấy đã thực hiện thay đổi tài liệu tương ứng để lưu ý rằng đó "0"+là một trường hợp hợp lệ cho decimalinteger(trước đó đã được đề cập trong octinteger).

Chúng ta có thể sẽ không bao giờ biết chính xác lý do tại sao Georg chọn làm cho "0"+hợp lệ - nó có thể mãi mãi vẫn là một trường hợp góc kỳ lạ trong Python.


CẬP NHẬT [28 tháng 7 năm 2015]: Câu hỏi này đã dẫn đến một chuỗi thảo luận sôi nổi về những ý tưởng về trăn mà Georg đã nói về :

Steven D'Aprano đã viết:

Tại sao nó được định nghĩa theo cách đó? [...] Tại sao chúng ta viết 0000 để nhận số 0?

Tôi có thể nói với bạn, nhưng sau đó tôi phải giết bạn.

Georg

Sau đó, luồng đã tạo ra báo cáo lỗi này nhằm mục đích loại bỏ trường hợp đặc biệt này. Đây, Georg nói :

Tôi không nhớ lý do cho sự thay đổi có chủ ý này (như đã thấy từ sự thay đổi tài liệu).

Tôi không thể đưa ra lý do chính đáng cho sự thay đổi này bây giờ [...]

và do đó chúng tôi có nó: lý do chính xác đằng sau sự mâu thuẫn này đã bị mất theo thời gian.

Cuối cùng, hãy lưu ý rằng báo cáo lỗi đã bị từ chối: các số không đứng đầu sẽ tiếp tục chỉ được chấp nhận trên các số nguyên 0 đối với phần còn lại của Python 3.x.


6
Tại sao bạn lại nói "Chúng tôi có thể sẽ không bao giờ biết chính xác tại sao Georg lại chọn ..."? Nếu ai đó biết anh ta nhìn thấy chủ đề này và thông báo cho anh ta về nó, thì anh ta có thể đưa ra câu trả lời của mình! (trừ khi bạn biết rằng anh ấy mãi mãi từ chối thảo luận về công việc Python trong quá khứ của mình hoặc một số trường hợp tương tự)
walrus

1
Tôi không hiểu tại sao họ không chỉ tạo octintegertrường hợp Python 2 thứ hai "0" octdigit*. 0là một ký tự bát phân trong C / C ++.
Ngẫu nhiên832

1
Thực ra tiếng Anh hơi mơ hồ trong vấn đề này. Từ "khác" có thể có nghĩa là "một nữa" hoặc nó có thể có nghĩa là "một khác". Một cách giải thích bằng tiếng Anh hợp lệ cho câu trích dẫn in đậm từ PEP 3127 có nghĩa là "Lỗi cú pháp sẽ xuất hiện bất cứ khi nào số '0' đứng đầu ngay sau đó là một chữ số khác '0'". Tôi không chắc đó có phải là ý định thực sự không ( mặc dù cách diễn giải đó dường như được mã thực tế hỗ trợ), nhưng trong mọi trường hợp, tôi không nghĩ là chính xác khi nói rằng PEP bị vi phạm về mặt kỹ thuật mà không cần giải thích rõ thêm về câu đó.
GrandOpener

2
@GrandOpener: Lưu ý rằng điều đó 001là bất hợp pháp, trong khi cách diễn giải của bạn sẽ cho thấy điều đó hợp pháp (vì nghĩa của "ngay lập tức" phải khá rõ ràng).
nneonneo

Điểm tốt. Vì vậy, PEP chắc chắn bị vi phạm; những gì mơ hồ là bản chất chính xác mà nó bị vi phạm. :)
GrandOpener

17

Đó là một trường hợp đặc biệt ( "0"+)

2.4.4. Chữ số nguyên

Các ký tự số nguyên được mô tả bằng các định nghĩa từ vựng sau:

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

Nếu bạn nhìn vào ngữ pháp, bạn sẽ dễ dàng thấy rằng 0cần một trường hợp đặc biệt. Tôi không chắc tại sao ' +' được coi là cần thiết ở đó. Đã đến lúc tìm hiểu danh sách gửi thư của nhà phát triển ...


Thật thú vị khi lưu ý rằng trong Python2, nhiều hơn một 0được phân tích cú pháp thành một octinteger(kết quả cuối cùng vẫn là như 0vậy)

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

1
Và bất kỳ ý tưởng tại sao có "0"+và không "0"?
lejlot,

1
@lejlot, chưa - nhưng tôi rất tò mò. Đó chắc chắn là một phần của mặc dù đặc tả
John La Rooy

3

Python2 đã sử dụng số 0 đứng đầu để chỉ định số bát phân:

>>> 010
8

Để tránh (? Gây hiểu lầm) hành vi này, Python3 đòi hỏi tiền tố rõ ràng 0b, 0o, 0x:

>>> 0o10
8

15
Câu hỏi vẫn là: tại sao được 00phép? (Và 000, 0000v.v.)
Michael Geary,

4
@MichaelGeary: có thể vì nó không thể mơ hồ (00000000 là 0 bất kể cơ sở nào) và việc xóa nó đi sẽ khiến mã không cần thiết? Vẫn lạ.
RemcoGerlich,

5
@RemcoGerlich Nếu tôi không sai, 01cũng là 1bất kể cơ sở.
Holt

2
@Holt: nhưng cho phép "0" + "1"? như một trường hợp đặc biệt có lẽ sẽ còn khó hiểu hơn.
RemcoGerlich,

4
@RemcoGerlich Chưa bao giờ nói là sẽ không;) Tôi chỉ nói rằng cái can't be ambiguouskhông phải là một đối số vì 01cũng không thể mơ hồ. IMO, 00trường hợp này chỉ là một trường hợp đặc biệt vì nó 0không nên như vậy.
Holt
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.