Чи забороняє Python два однакових ідентифікатори Unicode?


81

Я бавився з ідентифікаторами Unicode і натрапив на це:

>>> 𝑓, x = 1, 2
>>> 𝑓, x
(1, 2)
>>> 𝑓, f = 1, 2
>>> 𝑓, f
(2, 2)

Що тут відбувається? Чому Python замінює об’єкт, на який посилається 𝑓, але лише іноді? Де описана така поведінка?


9
Це цікаве питання, але вашим мінімальним відтворюваним прикладом міг бути щойно𝑓=1 f=2 print(𝑓)
khelwood

1
Дякую. Зробив приклад ще меншим зараз.
Ерік Седерстранд,


1
a, a = 1, 2; a, a. Це не має нічого спільного з fабо 𝑓.
user76284

4
Прикладу 𝑓 = 3; fбуло б достатньо.
user76284

Відповіді:


81

PEP 3131 - Зазначається підтримка ідентифікаторів, що не належать до ASCII

Усі ідентифікатори перетворюються в нормальну форму NFKC під час синтаксичного аналізу; порівняння ідентифікаторів базується на NFKC.

Ви можете використовувати unicodedataдля перевірки конверсій:

import unicodedata

unicodedata.normalize('NFKC', '𝑓')
# f

що означало б, що '𝑓'перетворюється на 'f'аналіз. Провідний до очікуваного:

𝑓  = "Some String"
print(f)
# "Some String"

23
Це чудова відповідь, але жахливе рішення основних розробників Python. Я зауважую, що під час обговорення цього PEP одним із заперечень було те, що Unicode недостатньо зрозумілий і має слабкі інструменти. Тепер, через десять років, я задаюся питанням, чи не пора переосмислити романізацію ідентифікаторів Unicode.
Адам Сміт

33
@AdamSmith, але нормалізація Unicode не є романізацією. Ви можете мати πідентифікатор Python, який відрізняється від pпросто чудового. Якщо я правильно розумію, складання NFK * стосується символів, які, як вважали люди Юнікоду, мали б бути однаковими, але їх не можна об’єднати через зворотну сумісність із деякими застарілими кодуваннями.
lenz

19
Існує два види еквівалентності характеру: канонічна та сумісність. Канонічна еквівалентність повинна надавати точно такий самий гліф, що не стосується 𝑓 та f. NFKC нормалізує як канонічну, так і сумісність, що, на мою думку, є поганим вибором для такої мови програмування, як Python, яка розрізняє регістри літер: очікується, що ідентифікатори, які відображаються по-різному, повинні бути різними. Python повинен був використовувати NFC, що забезпечує 𝑓 та f різні речі.
lvella

27
Певна форма нормалізації потрібна, наприклад, через латинські символи з діакритикою - якщо я бачу такий символ, як 'ü', то це може бути або складений символ (u + поєднує діарезу), або попередньо складений один символ; користувач не мав би розумного способу або бажання розрізнити їх, і бажаний спосіб введення, швидше за все, дозволив би ввести лише один із цих варіантів. Тож бажано, щоб, якщо я бачу 'ü' і набираю 'ü', тоді мова розглядає символи як еквівалентні, навіть якщо вони закодовані по-різному, хоча для цього, мабуть, нормалізації NFC.
Peteris

8
Python підтримує Unicode для ідентифікаторів, щоб полегшити його використання при визначенні ідентифікаторів неанглійськими мовами, а не для забезпечення рівного доступу до всіх точок коду Unicode. Наприклад, в даний час досить важко зламати парсер для підтримки операторів Unicode, оскільки будь-який символ, що не є ASCII, спочатку вважається частиною ідентифікатора, навіть якщо розглянутий символ Unicode не є допустимою частиною ідентифікатора. Ідея полягає не в підтримці майнінгу Unicode для "цікавих" символів, а в підтримці символів, створених стандартними не англійськими розкладками клавіатури.
chepner

28

Ось невеликий приклад, щоб показати, наскільки жахлива ця "особливість":

𝕋𝐡ᵢ𝔰_f𝔢𝘢𝚝𝓊ᵣₑ_𝕤ₕ𝔬𝔲𝖑𝔡_dₑ𝕗ᵢ𝘯i𝘵𝚎ℓy_𝒷𝘦_𝐚_𝚋ᵘg = 42
print(T𝗵ℹ𝚜_𝒇e𝖆𝚝𝙪ᵣe_ₛ𝔥º𝓾𝗹𝙙_𝚍e𝒇ᵢ𝒏ⁱtᵉ𝕝𝘆_𝖻ℯ_𝔞_𝖇𝖚𝓰)
# => 42

Спробуйте в Інтернеті! (Але, будь ласка, не використовуйте його)

І як згадував @MarkMeyer, два ідентифікатори можуть бути різними, хоча вони виглядають однаково ("КИРИЛІЧНА ВЕЛИКА БУКВА А" та "ЛАТИННА ВЕЛИКА БУКВА А")

А = 42
print(A)
# => NameError: name 'A' is not defined

3
Змушує мене написати еквівалент jsfuck.com ... python-unicode-hell.com?
Mathieu VIALES

2
@MathieuVIALES 𝓕𝕖𝒆𝑙 𝐟ʳ𝙚ₑ ᵗ𝗈 ᵈ𝚘 𝓈º. Я 𝐡a𝔳ᵉ 𝒔𝚘𝙢𝖾 𝒄𝑜𝖽ᵉ 𝖑𝒶𝒚𝑖𝒏𝕘 arₒ𝘶𝘯𝖽. 𝐈 ʷ𝙖n𝓉ℯ𝙙 𝒕𝘰 𝗍𝕣o𝑙𝗅 ⅽ𝔬𝚕𝘭ᵉ𝗮𝓰𝘶𝖊𝔰 ʷ𝚒ₜ𝙝 𝓲ᵗ, 𝕓𝒖t 𝚝ℎₑ 𝗋𝑒𝙨𝓊𝕝𝓉 ⅈ𝔰 𝓳ᵘ𝑠𝙩 t𝚘𝗈 𝗵o𝒓𝑟ible 𝘀𝐨 𝐼 ⁿ𝚎v𝖾𝔯 ᵘ𝓼ₑⅾ ⅈt. 𝕌𝓃𝗍𝚒l 𝕟𝚘𝙬.
Ерік Думініл

8
І тоді звичайно: А = 42; print(A)-> "NameError: ім'я 'A' не визначено"
Mark M

8
Суть полягала в тому, щоб ніколи не відкривати двері для довільно складних імен ідентифікаторів, а в тому, щоб полегшити введення ідентифікаторів рідною мовою програміста (використовуючи розкладку клавіатури, рідну для цієї мови). Краще використовувати класифікацію кодової точки Unicode як букву, ніж виконувати функції арбітра, для якого системи письма можуть і не можуть використовуватися для ідентифікаторів. (І обмеження ідентифікатора символами однієї системи письма далеко перевищує рівень оплати парсера.)
chepner

12
Жодна з цих кодових точок не є частиною жодної системи письма на будь-якій природній мові, тому чи є якась із них прийнятною як частина ідентифікатора майже "випадковою", заснованою на класифікації Unicode, а не на явному схваленні самого Python.
Чепнер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.