我如何检查一个标识符是 dunder 还是 class-private(即会被损坏)?

How can I check if an identifier is dunder or class-private (i.e. will be mangled)?

我正在编写一个提供有关变量名称建议的项目,我希望它能够判断某个名称是否与 reserved classes of identifiers 中的任何一个相匹配。第一个(“private”)非常简单,只是 name.startswith('_'),但是 dunder 和 class-private 名称更复杂。有没有内置函数可以告诉我?如果不是,Python 使用的内部规则是什么?

对于 dunder,检查 name.startswith('__') and name.endswith('__') 不起作用,因为那会匹配 '__'。也许像 ^__\w+__$ 这样的正则表达式可以工作?

对于 class-private,name.startswith('__') 不起作用,因为 dunder 名称没有被破坏,也没有像 '___' 这样只有下划线的名称。所以看起来我必须检查名称是否以两个下划线开头,不以两个下划线结尾,并且至少包含一个非下划线字符。那正确吗?在代码中:

name.startswith('__') and not name.endswith('__') and any(c != '_' for c in name)

我最关心的是边缘情况,所以我想确保我的规则 100% 正确。我读了 What is the meaning of single and double underscore before an object name?,但没有足够的细节。

这是内部规则。请注意,我几乎看不懂 C,所以对此持保留态度。

邓德

基于 is_dunder_name in Objects/typeobject.c(使用 Python 3.7 中的 str.isascii):

len(name) > 4 and name.isascii() and name.startswith('__') and name.endswith('__')

或者,正则表达式 ^__\w+__$ 可以工作,但需要启用 re.ASCII 以确保 \w 仅匹配 ASCII 字符。

Class-私有

基于_Py_Mangle in Python/compile.c:

name.startswith('__') and not name.endswith('__') and not '.' in name

尽管严格来说,带点的名称是“属性引用”,而不是名称,因此您可以删除该检查:

name.startswith('__') and not name.endswith('__')

这符合 Identifiers (Names).

下文档中的规则

(旁注:我没有意识到,但 not name.endswith('__') 确保该名称至少包含一个非下划线。)