要了解发生了什么,我们应该看看Python如何在language reference中定义标识符:
identifier ::= xid_start xid_continue*
id_start ::=
id_continue ::=
xid_start ::=
xid_continue ::=
我们的两个字符MICRO SIGN和GREEK SMALL LETTER MU都是Ll unicode组(小写字母)的一部分,因此它们都可以在标识符中的任何位置使用。现在请注意,标识符的定义实际上是指xid_start和xid_continue,并且它们被定义为相应的非x定义中的所有字符,其中NFKC归一化导致标识符的有效字符序列。
Python显然只关心标准化的标准化形式。这被确认了一下:
All identifiers are converted into the normal form NFKC while parsing; comparison of identifiers is based on NFKC.
NFKC是一个Unicode normalization,它将字符分解成各个部分。 MICRO SIGN分解为GREEK SMALL LETTER MU,这正是在那里发生的。
还有很多其他角色也受到这种规范化的影响。另一个例子是OHM SIGN,它分解为GREEK CAPITAL LETTER OMEGA.使用它作为标识符给出了类似的结果,这里使用本地显示:
>>> Ω = 'bar'
>>> locals()['Ω']
Traceback (most recent call last):
File "", line 1, in
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这样做,这也使我们能够从本地人()或(使用getattr)中正确地获取我们的价值:
>>> normalized_ohm = unicodedata.normalize('NFKC', 'Ω')
>>> locals()[normalized_ohm]
'bar'