Python 是否禁止使用两个相似的 Unicode 标识符?

Eri*_*and 84 python unicode

我在玩 Unicode 标识符并偶然发现了这一点:

>>> , x = 1, 2
>>> , x
(1, 2)
>>> , f = 1, 2
>>> , f
(2, 2)
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?为什么 Python 会替换 引用的对象,但只是有时?这种行为在哪里描述?

Mar*_*yer 87

PEP 3131——支持非 ASCII 标识符

所有标识符在解析时都转换为范式NFKC;标识符的比较基于 NFKC。

您可以使用unicodedata来测试转换:

import unicodedata

unicodedata.normalize('NFKC', '')
# f
Run Code Online (Sandbox Code Playgroud)

这将表明在解析中''被转换为'f'。导致预期:

  = "Some String"
print(f)
# "Some String"
Run Code Online (Sandbox Code Playgroud)

  • @AdamSmith 但 Unicode 规范化不是罗马化。您可以将“π”作为与“p”不同的Python标识符。如果我理解正确的话,NFK* 折叠是关于 Unicode 人员认为一开始应该是相同字符的字符,但由于与某些旧编码的向后兼容性,它们无法合并。 (35认同)
  • 需要某种形式的标准化,因为例如带有变音符号的拉丁字符 - 如果我看到像“ü”这样的字符,那么它可能是复合字符(u + 组合分音符)或预组合的单个字符;用户没有合理的方式或愿望来区分它们,并且他们的首选输入方法可能只允许输入这些选项之一。因此,如果我看到“ü”并输入“ü”,那么该语言会将这些字符视为等效字符,即使它们的编码方式不同,但 NFC 规范化可能就足够了。 (28认同)
  • 这是一个很好的答案,但却是 Python 核心开发人员做出的一个糟糕的决定。我注意到,在本 PEP 的讨论中,反对意见之一是 Unicode 理解甚少且工具薄弱。现在,十多年过去了,我想知道是否是时候重新考虑 Unicode 标识符的罗马化了。 (25认同)
  • 字符等效有两种:规范等效和兼容性等效。规范等价应呈现完全相同的字形,但 和 f 之间的情况并非如此。NFKC 规范化了规范和兼容性等价性,我同意这对于像 Python 这样区分字母大小写的编程语言来说是一个糟糕的选择:预计以不同方式呈现的标识符应该是不同的。Python 应该使用 NFC,这确保 和 f 是不同的东西。 (21认同)
  • Python 支持 Unicode 标识符,以便于在非英语语言中定义标识符时使用它,而不是提供对所有 Unicode 代码点的平等访问。例如,目前破解解析器以支持 Unicode 运算符非常困难,因为任何非 ASCII 字符首先被假定为标识符的一部分,即使所讨论的 Unicode 字符不是标识符的有效部分。这个想法不是支持挖掘 Unicode 中的“有趣”字符,而是支持标准非英语键盘布局生成的字符。 (9认同)
  • Unicode 网站 https://unicode.org/faq/normalization.html 上建议使用 NKFC 作为标识符 (6认同)
  • 像 `` 这样的字符是“意外”允许的,因为它们被归类为字母,即使没有自然语言将其用作其书写系统的一部分。 (4认同)

Eri*_*nil 31

这是一个小例子,只是为了说明这个“功能”是多么可怕:

?_???_?_d????y___?g = 42
print(T?_e?e_?º_e????_?__)
# => 42
Run Code Online (Sandbox Code Playgroud)

在线试试吧!(但请不要使用它)

正如@MarkMeyer 所提到的,即使两个标识符看起来相同,它们也可能是不同的(“CYRI​​LLIC CAPITAL LETTER A”和“LATIN CAPITAL LETTER A”)

? = 42
print(A)
# => NameError: name 'A' is not defined
Run Code Online (Sandbox Code Playgroud)

  • 这些代码点都不属于任何自然语言的书写系统,因此它们是否可以作为标识符的一部分被接受几乎是“偶然的”,基于 Unicode 分类,而不是 Python 本身的任何明确认可。 (13认同)
  • 重点从来不是打开任意复杂的标识符名称的大门,而是为了方便以程序员的母语(使用该语言的本机键盘布局)键入标识符。最好按照 Unicode 将代码点分类为字母,而不是充当书写系统可以或不能用于标识符的仲裁者。(将标识符限制为来自单一书写系统的字符远远超出了解析器的薪资等级。) (9认同)
  • 然后当然是:`А = 42; print(A)` --> "NameError: 名称 'A' 未定义" (8认同)
  • 让我想写一个相当于 jsfuck.com...python-unicode-hell.com 的东西? (3认同)
  • @MathieuVIALES ʳₑ ᵗ ᵈ º。我 ᵉ ᵉ arₒ。ʷnℯ o ⅽᵉ ʷₜ ᵗ,t ℎₑ ⅈ ᵘ t oible ⁿv ᵘₑⅾ ⅈt。升。 (2认同)
  • @chepner:谢谢。这对我来说(几乎)是有道理的,即使最终的行为可能真的令人惊讶。一般来说,Python 设计得非常好,所以这些怪癖就很突出。没有人会注意到或关心它是否只是另一个 JavaScript WTF。 (2认同)