2015-12-04 6 views
20

Я просто наткнулся на следующей нечетной ситуации:Идентификация нормализации: почему микро-знак преобразуется в греческую букву mu?

>>> class Test: 
     µ = 'foo' 

>>> Test.µ 
'foo' 
>>> getattr(Test, 'µ') 
Traceback (most recent call last): 
    File "<pyshell#4>", line 1, in <module> 
    getattr(Test, 'µ') 
AttributeError: type object 'Test' has no attribute 'µ' 
>>> 'µ'.encode(), dir(Test)[-1].encode() 
(b'\xc2\xb5', b'\xce\xbc') 

Персонаж я вошел всегда μ знак на клавиатуре, но по какой-то причине он преобразуется. Почему это происходит?

ответ

21

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

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

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 и ГРЕЧЕСКИХ строчной MU, являются частью группы Ll юникода (строчные буквы), поэтому оба они могут использоваться в любой позиции в идентификаторе. Теперь обратите внимание, что определение identifier действительно относится к xid_start и xid_continue, и они определяются как все символы в соответствующем не-х определении, чья нормализация NFKC приводит к допустимой последовательности символов для идентификатора.

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

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

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

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

>>> Ω = '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' 
+1

Это было очень ясно и основательно. Я по-прежнему стараюсь избегать символов, отличных от ASCII, даже в строковых литералах, не говоря уже об именах переменных. Нормализация - это всего лишь одна проблема, все может быть искажено некоторыми редакторами, скопировать и вставить изменения кодировки и т. Д. 'Class Test: mu = 'foo'' – Galax

+1

Пока вы используете UTF-8 для своих исходных файлов (которые вы действительно должен), в большинстве случаев вы отлично справляетесь с Python 3, особенно в строковых литералах. Если у вас есть редактор, который может испортить это, вы должны получить лучший редактор;) И что касается идентификаторов, вы можете быть там творческими, за исключением показанной проблемы, которая может вызвать проблемы для некоторых или совершенно незаметна для других :) – poke

 Смежные вопросы

  • Нет связанных вопросов^_^