Удивительное поведение с ключами unicode dict [duplicate]

Попробуйте использовать свободно доступный класс ImageInfo, я использовал его для этой же цели:

http://linux.softpedia.com/get/Multimedia/Graphics/ImageInfo-19792 .shtml

23
задан Peter O. 5 December 2015 в 00:43
поделиться

2 ответа

Здесь задействованы два разных персонажа. Один из них - MICRO SIGN , который находится на клавиатуре, а другой - GREEK SMALL LETTER MU .

Чтобы понять, что происходит, мы должны посмотреть, как Python определяет идентификаторы в справочнике языка :

identifier   ::=  xid_start xid_continue*
id_start     ::=  <all characters in general categories Lu, Ll, Lt, Lm, Lo, Nl, the underscore, and characters with the Other_ID_Start property>
id_continue  ::=  <all characters in id_start, plus characters in the categories Mn, Mc, Nd, Pc and others with the Other_ID_Continue property>
xid_start    ::=  <all characters in id_start whose NFKC normalization is in "id_start xid_continue*">
xid_continue ::=  <all characters in id_continue whose NFKC normalization is in "id_continue*">

Оба наших символа, MICRO SIGN и GREEK SMALL LETTER MU являются частью Ll unicode group (строчные буквы), поэтому оба они могут использоваться в любой позиции в идентификаторе. Теперь обратите внимание, что определение identifier на самом деле относится к xid_start и xid_continue, и они определены как все символы в соответствующем не-х определении, чья нормализация NFKC приводит к допустимой последовательности символов для идентификатора.

Очевидно, что Python заботится только о нормализованной форме идентификаторов . Это подтверждается немного ниже:

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

NFKC - это нормализация Юникода , которая разлагает символы на отдельные части. MICRO SIGN распадается на GREEK SMALL LETTER MU, и это именно то, что происходит там.

На эту нормализацию также влияет множество других символов. Другим примером является OHM SIGN , который распадается на GREEK CAPITAL LETTER OMEGA . Использование этого в качестве идентификатора дает аналогичный результат, здесь показано с использованием locals:

>>> Ω = 'bar'
>>> locals()['Ω']
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    locals()['Ω']
KeyError: 'Ω'
>>> [k for k, v in locals().items() if v == 'bar'][0].encode()
b'\xce\xa9'
>>> 'Ω'.encode()
b'\xe2\x84\xa6'

Итак, в конце концов, это именно то, что делает Python. К сожалению, на самом деле нет хорошего способа обнаружить это поведение, вызывая ошибки, такие как показанные. Обычно, когда идентификатор упоминается только как идентификатор, то есть он используется как реальная переменная или атрибут, тогда все будет нормально: нормализация выполняется каждый раз, и идентификатор найден.

Единственный проблема связана со строковым доступом. Строки - это просто строки, конечно, нормализации не происходит (это будет просто плохая идея). И два способа, показанные здесь, getattr и locals , оба действуют на словари. getattr() получает доступ к атрибуту объекта через __dict__ объекта, а locals() возвращает словарь. И в словарях клавишами может быть любая строка, поэтому вполне нормально иметь MICRO SIGN или OHM SIGN там.

В этих случаях вам нужно помнить о том, чтобы выполнить нормализацию самостоятельно. Для этого мы можем использовать unicodedata.normalize , что также позволяет нам правильно получить наше значение изнутри locals() (или используя getattr):

>>> normalized_ohm = unicodedata.normalize('NFKC', 'Ω')
>>> locals()[normalized_ohm]
'bar'
24
ответ дан poke 20 August 2018 в 11:47
поделиться
  • 1
    Это было очень ясно и основательно. Я по-прежнему стараюсь избегать символов, отличных от ASCII, даже в строковых литералах, не говоря уже об именах переменных. Нормализация - это всего лишь одна проблема, все может быть искажено некоторыми редакторами, копией и amp; вставка, изменяющая кодировку и т. д. class Test: mu = 'foo' – Galax 4 December 2015 в 22:02
  • 2
    Пока вы используете UTF-8 для своих исходных файлов (что вам действительно нужно), в большинстве случаев с Python 3 отлично, особенно в строковых литералах. Если у вас есть редактор, который может испортить это, вы должны получить лучший редактор;) И что касается идентификаторов, вы можете быть там творческими, за исключением показанной проблемы, которая может вызвать проблемы для некоторых или совершенно незаметна для других :) – poke 4 December 2015 в 22:08

Что здесь делает Python на основе Стандартное приложение Unicode № 31 :

Реализации, которые принимают нормализацию и учитывают случай, имеют два выбор: рассматривать варианты как эквивалентные или запрещать варианты.

Остальная часть этого раздела дает более подробную информацию, но в основном это означает, что если язык позволяет вам иметь идентификатор с именем µ, он должен обрабатывать два символа µ MICRO SIGN и GREEK SMALL LETTER MU, и он должен делать это, рассматривая их как GREEK SMALL LETTER MU.


Most другие языки, которые допускают не-ASCII-идентификаторы, следуют одному и тому же стандарту: 1 только несколько языков придумали свои собственные2. Таким образом, это правило имеет то преимущество, что оно одинаково на самых разных языках (и, возможно, оно поддерживается IDE и другими инструментами ).

Можно было бы сделать вывод, что он действительно не работает на языке, как отраженный, как Python, где строки могут использоваться как идентификатор так же легко, как писать getattr(Test, 'µ'). Но если вы можете прочитать обсуждение рассылки python-3000 , вокруг PEP 3131 ; единственное, что серьезно рассматривалось, - это придерживаться ASCII, UAX-31 или незначительных вариаций Java на UAX-31; никто не хотел изобретать новый стандарт только для Python.

Другим способом решения этой проблемы было бы добавить тип collections.identifierdict, который документирован для применения тех же правил поиска, которые компилятор применяет для идентификаторов в источнике, и использовать этот тип в сопоставлениях, предназначенных для использования в качестве пространств имен (например, объект, модуль, локали, определения классов). Я смутно помню, что кто-то предлагал это, но не имел хороших мотивирующих примеров. Если кто-то считает, что это хороший пример для оживления идеи, они могут опубликовать ее на bugs.python.org или списке идей python .

< hr>

1. Некоторые языки, такие как ECMAScript и C #, вместо этого используют «стандарт Java», который основан на ранней форме UAX-31 и добавляет некоторые незначительные расширения, такие как игнорирование управляющих кодов RTL, но это достаточно близко.

2. Например, Julia позволяет использовать символы Unicode и математические символы, а также имеет правила для сопоставления между идентификаторами LaTeX и Unicode, но они явно добавили правила для нормализации ɛ и µ к греческим ...

0
ответ дан abarnert 20 August 2018 в 11:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: