str.isidentifier()
函数可以工作。使用正则表达式可能会出现以下问题:未能匹配一些有效的Python标识符,或者错误地匹配了一些无效的标识符。
str.isidentifier()
函数会根据语言定义中标识符和关键字部分中的规定返回字符串是否为有效标识符。
使用keyword.iskeyword()
函数测试保留标识符,例如def和class。
@martineau的评论给出了'℘᧚'
的例子,其中正则表达式解决方案会失败。
>>> '℘᧚'.isidentifier()
True
>>> import re
>>> bool(re.search(r'^[^\d\W]\w*\Z', '℘᧚'))
False
为什么会发生这种情况?
让我们定义与给定正则表达式匹配的代码点集合和与 str.isidentifier
匹配的集合。
import re
import unicodedata
chars = {chr(i) for i in range(0x10ffff) if re.fullmatch(r'^[^\d\W]\w*\Z', chr(i))}
identifiers = {chr(i) for i in range(0x10ffff) if chr(i).isidentifier()}
有多少个正则表达式匹配不是标识符?
In [26]: len(chars - identifiers)
Out[26]: 698
有多少个标识符不匹配正则表达式?
In [27]: len(identifiers - chars)
Out[27]: 4
有趣——哪些?
In [37]: {(c, unicodedata.name(c), unicodedata.category(c)) for c in identifiers - chars}
Out[37]:
set([
('\u1885', 'MONGOLIAN LETTER ALI GALI BALUDA', 'Mn'),
('\u1886', 'MONGOLIAN LETTER ALI GALI THREE BALUDA', 'Mn'),
('℘', 'SCRIPT CAPITAL P', 'Sm'),
('℮', 'ESTIMATED SYMBOL', 'So'),
])
这两个集合有什么不同之处?
它们具有不同的Unicode“通用类别”值。
In [31]: {unicodedata.category(c) for c in chars - identifiers}
Out[31]: set(['Lm', 'Lo', 'No'])
根据维基百科,这是指Letter, modifier
(字母,修饰符)、Letter, other
(字母,其他)和Number, other
(数字,其他)。这与re文档一致,因为\d
仅匹配十进制数字:
\d
匹配任何Unicode十进制数字(即,在Unicode字符类别[Nd]中的任何字符)
其他情况呢?
In [32]: {unicodedata.category(c) for c in identifiers - chars}
Out[32]: set(['Mn', 'Sm', 'So'])
那是Mark, nonspacing
; Symbol, math
; Symbol, other
。
这都在哪里记录?
它在哪里实现了?
https://github.com/python/cpython/commit/47383403a0a11259acb640406a8efc38981d2255
我仍然需要一个正则表达式
看一下 PyPI 上的 regex 模块。
这个正则表达式实现与标准的're'模块向后兼容,但提供了额外的功能。
它包括“通用类别”过滤器。