Mengapa Python 3 mengizinkan "00" sebagai literal untuk 0 tetapi tidak mengizinkan "01" sebagai literal untuk 1?

111

Mengapa Python 3 mengizinkan "00" sebagai literal untuk 0 tetapi tidak mengizinkan "01" sebagai literal untuk 1? Apakah ada alasan yang bagus? Ketidakkonsistenan ini membuatku bingung. (Dan kita berbicara tentang Python 3, yang dengan sengaja merusak kompatibilitas ke belakang untuk mencapai tujuan seperti konsistensi.)

Sebagai contoh:

>>> from datetime import time
>>> time(16, 00)
datetime.time(16, 0)
>>> time(16, 01)
  File "<stdin>", line 1
    time(16, 01)
              ^
SyntaxError: invalid token
>>>
anjing laut
sumber
42
Ini tidak dapat dihapus sekarang, atau itu akan merusak kompatibilitas dengan pertanyaan ini!
John La Rooy

Jawaban:

103

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

Literal bilangan bulat dijelaskan oleh definisi leksikal berikut:

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"

Tidak ada batasan untuk panjang literal integer selain dari apa yang dapat disimpan dalam memori yang tersedia.

Perhatikan bahwa nol di depan dalam angka desimal bukan nol tidak diperbolehkan. Ini untuk disambiguasi dengan literal oktal gaya-C, yang digunakan Python sebelum versi 3.0.

Seperti disebutkan di sini, nol di depan dalam angka desimal bukan nol tidak diperbolehkan. "0"+legal sebagai kasus yang sangat khusus, yang tidak ada di Python 2 :

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

SVN commit r55866 mengimplementasikan PEP 3127 di tokenizer, yang melarang 0<octal>nomor lama . Namun, anehnya, itu juga menambahkan catatan ini:

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

dengan nonzerobendera khusus yang hanya melempar SyntaxErrorjika urutan digit berikut berisi digit bukan nol.

Ini aneh karena PEP 3127 tidak mengizinkan kasus ini:

PEP ini mengusulkan bahwa kemampuan untuk menentukan bilangan oktal dengan menggunakan nol di depan akan dihapus dari bahasa di Python 3.0 (dan mode pratinjau Python 3.0 2.6), dan bahwa SyntaxError akan dimunculkan setiap kali awalan "0" adalah segera diikuti dengan digit lain .

(penekanan saya)

Jadi, fakta bahwa beberapa angka nol diperbolehkan secara teknis melanggar PEP, dan pada dasarnya diterapkan sebagai kasus khusus oleh Georg Brandl. Dia membuat perubahan dokumentasi terkait untuk mencatat bahwa "0"+itu adalah kasus yang valid decimalinteger(sebelumnya yang telah dibahas di bawah octinteger).

Kita mungkin tidak akan pernah tahu persis mengapa Georg memilih untuk membuat "0"+valid - itu mungkin selamanya tetap menjadi kasus sudut aneh di Python.


PEMBARUAN [28 Jul 2015]: Pertanyaan ini mengarah ke utas diskusi yang hidup tentang ide-python di mana Georg menimpali :

Steven D'Aprano menulis:

Mengapa didefinisikan seperti itu? [...] Mengapa kita menulis 0000 untuk mendapatkan nol?

Aku bisa memberitahumu, tapi kemudian aku harus membunuhmu.

Georg

Kemudian, utas memunculkan laporan bug ini yang bertujuan untuk menyingkirkan kasus khusus ini. Di sini, Georg berkata :

Saya tidak ingat alasan perubahan yang disengaja ini (seperti yang terlihat dari perubahan dokumen).

Saya tidak dapat menemukan alasan yang tepat untuk perubahan ini sekarang [...]

dan demikianlah kita memilikinya: alasan yang tepat di balik ketidakkonsistenan ini hilang seiring waktu.

Akhirnya, perhatikan bahwa laporan bug ditolak: nol di depan akan terus diterima hanya pada bilangan bulat nol untuk sisa Python 3.x.

nneonneo
sumber
6
Mengapa Anda mengatakan "Kita mungkin tidak akan pernah tahu persis mengapa Georg memilih untuk ..."? Jika seseorang yang mengenalnya melihat utas ini dan memberitahunya tentang hal itu, maka dia mungkin datang memberikan jawabannya! (kecuali Anda tahu dia selamanya menolak untuk membahas pekerjaan Python masa lalunya, atau keadaan serupa)
walrus
1
Saya tidak mengerti mengapa mereka tidak hanya membuat octintegercase Python 2 kedua "0" octdigit*. 0adalah literal oktal di C / C ++.
Random832
1
Sebenarnya bahasa Inggris agak ambigu dalam hal ini. Kata "yang lain" bisa berarti "satu lagi" atau bisa berarti "yang berbeda". Satu interpretasi bahasa Inggris yang valid dari kutipan yang dicetak tebal dari PEP 3127 adalah berarti "SyntaxError akan dimunculkan setiap kali awalan '0' segera diikuti dengan digit selain '0'" Saya tidak yakin apakah itu yang sebenarnya dimaksudkan ( meskipun interpretasi tersebut tampaknya didukung oleh kode yang sebenarnya), tetapi dalam hal apa pun menurut saya tidak tepat untuk mengatakan bahwa PEP secara teknis dilanggar tanpa klarifikasi tambahan dari kalimat tersebut.
GrandOpener
2
@GrandOpener: Perhatikan bahwa 001itu ilegal, sedangkan interpretasi Anda akan membuatnya legal (karena arti "segera" harus cukup jelas).
nneonneo
Poin yang bagus. Jadi PEP sudah pasti dilanggar; apa yang ambigu adalah sifat tepat di mana hal itu dilanggar. :)
GrandOpener
17

Ini kasus khusus ( "0"+)

2.4.4. Literal bilangan bulat

Literal bilangan bulat dijelaskan oleh definisi leksikal berikut:

integer :: = decimalinteger | octinteger | hexinteger | bininteger.dll
decimalinteger :: = digit nonzerodigit * | "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"

Jika Anda melihat tata bahasanya, sangat mudah untuk melihat bahwa 0perlu kasus khusus. Saya tidak yakin mengapa ' +' dianggap perlu di sana. Saatnya menggali milis dev ...


Menarik untuk dicatat bahwa di Python2, lebih dari satu 0diurai sebagai octinteger(hasil akhirnya masih 0)

decimalinteger :: = digit nonzerodigit * | "0"
octinteger :: = "0" ("o" | "O") octdigit + | "0" octdigit +
John La Rooy
sumber
1
Dan tahu mengapa ada "0"+dan tidak "0"?
lejlot
1
@lejlot, belum - tapi saya tertarik. Ini jelas merupakan bagian dari spesifikasi
John La Rooy
3

Python2 menggunakan nol di depan untuk menentukan bilangan oktal:

>>> 010
8

Untuk menghindari hal ini (? Menyesatkan) perilaku, Python3 membutuhkan awalan eksplisit 0b, 0o, 0x:

>>> 0o10
8
dlask
sumber
15
Pertanyaannya tetap: mengapa 00diperbolehkan? (Dan 000,, 0000dll.)
Michael Geary
4
@MichaelGeary: mungkin karena itu tidak bisa ambigu (00000000 adalah 0 terlepas dari basisnya) dan menghapusnya akan merusak kode? Masih aneh.
RemcoGerlich
5
@RemcoGerlich Jika saya tidak salah, 01juga 1terlepas dari dasar.
Holt
2
@Holt: tetapi mengizinkan "0" + "1"? sebagai kasus khusus mungkin akan lebih membingungkan.
RemcoGerlich
4
@RemcoGerlich Tidak pernah mengatakan tidak akan;) Saya hanya mengatakan bahwa can't be ambiguousitu bukan argumen karena 01juga tidak bisa ambigu. IMO, 00kasusnya hanyalah kasus khusus karena memang 0seharusnya tidak demikian.
Holt