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