Python 标识符规范化:为什么将微符号转换为希腊字母mu?

Python 标识符规范化:为什么将微符号转换为希腊字母mu?,python,python-3.x,unicode,identifier,python-internals,Python,Python 3.x,Unicode,Identifier,Python Internals,我只是偶然发现了以下奇怪的情况: >>> class Test: µ = 'foo' >>> Test.µ 'foo' >>> getattr(Test, 'µ') Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> getattr(Test, 'µ') AttributeError: typ

我只是偶然发现了以下奇怪的情况:

>>> 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')
>类测试:
µ=‘foo’
>>>测试。µ
“福”
>>>getattr(测试“µ”)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
getattr(测试“µ”)
AttributeError:类型对象“测试”没有属性“µ”
>>>“µ”.encode(),目录(测试)[-1].encode()
(b'\xc2\xb5',b'\xce\xbc')

我输入的字符始终是键盘上的µ号,但由于某些原因,它会被转换。为什么会发生这种情况?

这里涉及两个不同的角色。一个是键盘上的,另一个是

为了了解发生了什么,我们应该看看Python是如何定义标识符的:

所以最后,这只是Python所做的事情。不幸的是,没有真正的好方法来检测这种行为,从而导致如图所示的错误。通常,当标识符仅被称为标识符时,也就是说,它像实际变量或属性一样使用,那么一切都会很好:每次都会运行规范化,并且会找到标识符

唯一的问题是基于字符串的访问。字符串只是字符串,当然不会发生规范化(这只是个坏主意)。这里显示的两种方法都是在字典上运行的
getattr()
通过对象的
\uuuuu dict\uuuu
访问对象的属性,并且
locals()
返回字典。在字典里,键可以是任何字符串,所以在里面有一个微符号或欧姆符号是很好的

在这些情况下,您需要记住自己执行规范化。我们可以利用它,这也允许我们从内部
locals()
(或使用
getattr
)正确获取我们的值:

基于:

考虑规范化和大小写的实现有两种选择:将变量视为等效变量,或不允许变量

本节的其余部分给出了进一步的细节,但基本上,这意味着如果一种语言允许您有一个名为
µ
的标识符,它应该将这两个
µ
字符MICRO SIGN和希腊文小写字母MU视为同一个字符,并且应该将它们都视为希腊文小写字母MU


大多数允许非ASCII标识符的其他语言遵循相同的标准;1只有少数几种语言发明了自己的语言。2因此,此规则的优点是在各种语言中都是相同的(并且可能受到IDE和其他工具的支持)

有一种情况是,它在Python这样的反射性很强的语言中确实不能很好地工作,在Python中,字符串可以像编写
getattr(Test,'µ')
一样轻松地用作标识符。但是,如果你能阅读,你可以四处看看;唯一认真考虑的选择是坚持使用ASCII、UAX-31或Java在UAX-31上的微小变化;没有人想仅仅为Python发明一个新标准

解决此问题的另一种方法是添加一个
collections.identifierdict
类型,该类型被记录为应用编译器在源代码中应用于标识符的完全相同的查找规则,并在打算用作名称空间的映射中使用该类型(例如,对象、模块、局部变量、类定义)。我模模糊糊地记得有人这样建议,但没有任何好的激励例子。如果有人认为这是一个很好的例子,足以让这个想法复活,他们可以把它贴在或上


一,。有些语言,如ECMAScript和C#,使用“Java标准”,它基于早期形式的UAX-31,并添加了一些小的扩展,如忽略RTL控制代码,但这已经足够接近了


二,。例如,允许使用Unicode货币和数学符号,还具有LaTeX和Unicode标识符之间映射的规则,但他们明确添加了规则,将希腊latters规范化为
ɛ
µ

,这非常清楚和彻底。我仍然试图避免使用非ASCII字符,即使是在字符串文本中,更不用说变量名了。规范化只是一个问题,一些编辑器、复制和粘贴更改编码等也可能会弄糟事情。
类测试:mu='foo'
只要您对源文件使用UTF-8(您确实应该这样做),在大多数情况下使用Python 3都可以,尤其是在字符串文本方面。如果你有一个能把事情搞砸的编辑器,你应该找一个更好的编辑器;)至于标识符,您也可以在这里发挥创意,除了所显示的问题,它可能会给一些人带来问题,或者让其他人完全忽略:)
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*">
>>> Ω = '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'
>>> normalized_ohm = unicodedata.normalize('NFKC', 'Ω')
>>> locals()[normalized_ohm]
'bar'