Запрещает ли 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 09

4
𝑓 = 3; fДостаточно примера .
user76284 09

Ответы:


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.
Adam Smith

33
@AdamSmith, но нормализация Unicode - это не латинизация. У вас может быть πидентификатор Python, отличный от pпросто отличного . Если я правильно понимаю, сворачивание NFK * касается символов, которые, по мнению специалистов по Unicode, изначально должны были быть одним и тем же символом, но их нельзя объединить из-за обратной совместимости с некоторыми устаревшими кодировками.
lenz 08

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

27
Некоторая форма нормализации необходима из-за, например, латинских символов с диакритическими знаками - если я вижу такой символ, как «ü», то это может быть либо составной символ (u +, объединяющий диэрезис), либо предварительно составленный одиночный символ; у пользователя не будет разумного способа или желания различать их, и их предпочтительный метод ввода, вероятно, позволит ввести только один из этих вариантов. Поэтому желательно, чтобы если я увидел «ü» и набрал «ü», тогда язык считал символы эквивалентными, даже если они закодированы по-другому, хотя для этого, вероятно, будет достаточно нормализации NFC.
Петерис

8
Python поддерживает Unicode для идентификаторов, чтобы облегчить его использование при определении идентификаторов на неанглийских языках, а не для обеспечения равного доступа ко всем кодовым точкам Unicode. Например, в настоящее время довольно сложно взломать синтаксический анализатор для поддержки операторов Unicode, потому что любой не-ASCII символ сначала считается частью идентификатора, даже если рассматриваемый символ Unicode не является допустимой частью идентификатора. Идея состоит не в том, чтобы поддерживать поиск «интересных» символов в Unicode, а в том, чтобы поддерживать символы, созданные с помощью стандартных неанглийских раскладок клавиатуры.
chepner

28

Вот небольшой пример, чтобы показать, насколько ужасна эта «особенность»:

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

Попробуйте онлайн! (Но, пожалуйста, не используйте это)

И, как упомянуто @MarkMeyer, два идентификатора могут быть разными, даже если они выглядят одинаково («Кириллическая ЗАГЛАВНАЯ БУКВА A» и «ЛАТИНСКАЯ ЗАГЛАВНАЯ БУКВА A»)

А = 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 кодовой точки как буквы, чем действовать в качестве арбитра, для которого системы письма могут и не могут использоваться для идентификаторов. (И ограничение идентификатора символами из одной системы письма далеко выходит за рамки уровня оплаты парсера.)
Чепнер

12
Ни одна из этих кодовых точек не является частью системы письма любого естественного языка, поэтому вопрос о том, приемлемы ли какие-либо из них как часть идентификатора, является почти «случайным», основываясь на классификации Unicode, а не на каком-либо явном одобрении со стороны самого Python.
Чепнер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.