Unicode индексы и надстрочные индексы в идентификаторах, почему Python рассматривает XU == Xᵘ == Xᵤ?

Python допускает идентификаторы Unicode. Я определил Xᵘ = 42, ожидая XU и Xᵤ, чтобы привести к NameError. Но на самом деле, когда я определяю Xᵘ, Python (бесшумно?) Превращает Xᵘ в XU, что кажется мне чем-то вроде неряхотворной вещи. Почему это происходит?

>>> Xᵘ = 42
>>> print((Xu, Xᵘ, Xᵤ))
(42, 42, 42)

Ответ 1

Python преобразует все идентификаторы в свою нормальную форму NFKC; из раздела Идентификаторы справочной документации:

Все идентификаторы преобразуются в нормальную форму NFKC во время разбора; сравнение идентификаторов основано на NFKC.

Форма NFKC как надстрочного, так и нижестоящего символов является строчной u:

>>> import unicodedata
>>> unicodedata.normalize('NFKC', 'Xᵘ Xᵤ')
'Xu Xu'

Итак, в конце концов, все, что у вас есть, - это один идентификатор, Xu:

>>> import dis
>>> dis.dis(compile('Xᵘ = 42\nprint((Xu, Xᵘ, Xᵤ))', '', 'exec'))
  1           0 LOAD_CONST               0 (42)
              2 STORE_NAME               0 (Xu)

  2           4 LOAD_NAME                1 (print)
              6 LOAD_NAME                0 (Xu)
              8 LOAD_NAME                0 (Xu)
             10 LOAD_NAME                0 (Xu)
             12 BUILD_TUPLE              3
             14 CALL_FUNCTION            1
             16 POP_TOP
             18 LOAD_CONST               1 (None)
             20 RETURN_VALUE

Вышеприведенная разборка скомпилированного байт-кода показывает, что идентификаторы были нормализованы во время компиляции; это происходит во время разбора, любые идентификаторы нормализуются при создании AST (абстрактного дерева синтаксического анализа), который компилятор использует для создания байт-кода.

Идентификаторы нормализуются, чтобы избежать многих потенциальных "похожих похожих" ошибок, где в противном случае вы могли бы использовать оба find() (используя U + FB01 LATIN SMALL LIGATURE FI, за которым следуют символы ASCII nd) и find(), и задайтесь вопросом, почему ваш код имеет ошибку.

Ответ 2

Python, начиная с версии 3.0, поддерживает не-ASCII-идентификаторы. При анализе идентификаторы преобразуются с использованием нормализации NFKC, и любые идентификаторы, в которых нормализованное значение одинаковое, считаются одним и тем же идентификатором.

Подробнее см. PEP 3131. https://www.python.org/dev/peps/pep-3131/