如何在Python 2.6中获得isidentifier()功能?
Python 3 有一个字符串的方法,叫做 str.isidentifier
。
我想在 Python 2.6 中实现类似的功能,但不想自己重新写正则表达式之类的东西,应该怎么做呢?
8 个回答
re.match(r'[a-z_]\w*$', s, re.I)
这样做应该没问题。根据我所知道的,似乎没有什么内置的方法可以直接使用。
无效标识符验证
这个讨论中的所有回答似乎都在重复一个错误,就是在验证时允许一些不合法的字符串被当作合法标识符。
其他回答中提到的正则表达式模式是基于 tokenize.Name
,它包含了以下正则表达式模式 [a-zA-Z_]\w*
(运行在python 2.7.15上)和'$'这个正则锚点。
请参考官方的Python 3标识符和关键字说明(其中有一段内容也适用于Python 2)。
在ASCII范围内(U+0001..U+007F),合法的标识符字符和Python 2.x是一样的:大写和小写字母A到Z,下划线_,以及除了第一个字符外的数字0到9。
因此,'foo\n' 不应该被认为是一个合法的标识符。
虽然有人可能会争辩说这段代码是有效的:
>>> class Foo():
>>> pass
>>> f = Foo()
>>> setattr(f, 'foo\n', 'bar')
>>> dir(f)
['__doc__', '__module__', 'foo\n']
>>> print getattr(f, 'foo\n')
bar
因为换行符确实是一个有效的ASCII字符,但它不被视为字母。此外,显然以换行符结尾的标识符没有实际用途。
>>> f.foo\n
SyntaxError: unexpected character after line continuation character
str.isidentifier
函数也确认这不是一个合法的标识符:
python3解释器:
>>> print('foo\n'.isidentifier())
False
$
锚点与\Z
锚点
$
匹配字符串的结尾,或者在字符串结尾的换行符之前,在多行模式下也匹配换行符之前的内容。foo可以匹配‘foo’和‘foobar’,而正则表达式foo$只匹配‘foo’。更有趣的是,在'foo1\nfoo2\n'中搜索foo.$会正常匹配‘foo2’,但在多行模式下会匹配‘foo1’;在'foo\n'中搜索单个$会找到两个(空的)匹配:一个在换行符之前,另一个在字符串的结尾。
这导致以换行符结尾的字符串被错误地匹配为合法标识符:
>>> import tokenize
>>> import re
>>> re.match(tokenize.Name + '$', 'foo\n')
<_sre.SRE_Match at 0x3eac8e0>
>>> print m.group()
'foo'
正则表达式模式不应该使用$
锚点,而应该使用\Z
锚点。再次引用:
\Z
仅匹配字符串的结尾。
现在这个正则表达式就是合法的了:
>>> re.match(tokenize.Name + r'\Z', 'foo\n') is None
True
危险的隐患
请参见Luke的回答,了解这种弱正则匹配在其他情况下可能带来的更危险的后果。
进一步阅读
Python 3增加了对非ASCII标识符的支持,详见PEP-3131。
tokenize模块定义了一个叫做Name的正则表达式
import re, tokenize, keyword
re.match(tokenize.Name + '$', somestr) and not keyword.iskeyword(somestr)