pok*_*oke 25 python unicode identifier python-3.x python-internals
我偶然发现了以下奇怪的情况:
>>> 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')
Run Code Online (Sandbox Code Playgroud)
我输入的字符始终是键盘上的μ符号,但由于某种原因它会被转换.为什么会这样?
pok*_*oke 27
这里涉及两个不同的角色.一个是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*">
Run Code Online (Sandbox Code Playgroud)
我们的字符MICRO SIGN和GREEK SMALL LETTER MU都是Ll
unicode组(小写字母)的一部分,因此它们都可以在标识符的任何位置使用.现在请注意,identifier
实际引用的定义是xid_start
和xid_continue
,并且它们被定义为相应的非x定义中的所有字符,其NFKC规范化导致标识符的有效字符序列.
Python显然只关心标准化的标准化形式.这有点如下:
解析时,所有标识符都转换为正常格式NFKC; 标识符的比较基于NFKC.
NFKC是一种Unicode规范化,可将字符分解为单个部分.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'
Run Code Online (Sandbox Code Playgroud)
所以最后,这只是Python所做的事情.不幸的是,没有一种很好的方法可以检测到这种行为,从而导致错误,例如显示的错误.通常,当标识符仅被称为标识符时,即它像真实变量或属性一样使用时,一切都会正常:标准化每次运行,并找到标识符.
唯一的问题是基于字符串的访问.字符串只是字符串,当然没有规范化发生(这只是一个坏主意).这里显示的两种方式,getattr
并且locals
,无论在字典操作.getattr()
通过对象访问对象的属性__dict__
,并locals()
返回一个字典.在字典中,键可以是任何字符串,因此在那里有一个MICRO SIGN或OHM SIGN是完全没问题的.
在这些情况下,您需要记住自己执行标准化.我们可以利用unicodedata.normalize
它,这也允许我们从内部locals()
(或使用getattr
)正确地获取我们的价值:
>>> normalized_ohm = unicodedata.normalize('NFKC', '?')
>>> locals()[normalized_ohm]
'bar'
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
792 次 |
最近记录: |