Python 3 有一个名为的字符串方法str.isidentifier
我怎样才能在 Python 2.6 中获得类似的功能,而不是重写我自己的正则表达式等?
Python 3 有一个名为的字符串方法str.isidentifier
我怎样才能在 Python 2.6 中获得类似的功能,而不是重写我自己的正则表达式等?
tokenize 模块定义了一个名为 Name 的正则表达式
import re, tokenize, keyword
re.match(tokenize.Name + '$', somestr) and not keyword.iskeyword(somestr)
该线程中的所有答案似乎都在重复验证中的错误,该错误允许无效标识符的字符串像字符串一样匹配。
其他答案中建议的正则表达式模式是从中构建的,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
锚
$
匹配字符串的结尾或字符串结尾的换行符之前,并且在 MULTILINE 模式下也匹配换行符之前。foo 匹配 'foo' 和 'foobar',而正则表达式 foo$ 只匹配 'foo'。更有趣的是,在 'foo1\nfoo2\n' 中搜索 foo.$ 通常匹配 'foo2',但在 MULTILINE 模式下搜索 '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。
re.match(r'[a-z_]\w*$', s, re.I)
应该做得很好。据我所知,没有任何内置方法。
在 Python < 3.0 中,这很容易,因为标识符中不能包含 unicode 字符。那应该做的工作:
import re
import keyword
def isidentifier(s):
if s in keyword.kwlist:
return False
return re.match(r'^[a-z_][a-z0-9_]*$', s, re.I) is not None
到目前为止很好的答案。我会这样写。
import keyword
import re
def isidentifier(candidate):
"Is the candidate string an identifier in Python 2.x"
is_not_keyword = candidate not in keyword.kwlist
pattern = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
matches_pattern = bool(pattern.match(candidate))
return is_not_keyword and matches_pattern
我决定再试一次,因为有几个很好的建议。我会努力巩固它们。以下可以保存为 Python 模块并直接从命令行运行。如果运行,它会测试功能,因此可以证明是正确的(至少在文档演示该功能的范围内)。
import keyword
import re
import tokenize
def isidentifier(candidate):
"""
Is the candidate string an identifier in Python 2.x
Return true if candidate is an identifier.
Return false if candidate is a string, but not an identifier.
Raises TypeError when candidate is not a string.
>>> isidentifier('foo')
True
>>> isidentifier('print')
False
>>> isidentifier('Print')
True
>>> isidentifier(u'Unicode_type_ok')
True
# unicode symbols are not allowed, though.
>>> isidentifier(u'Unicode_content_\u00a9')
False
>>> isidentifier('not')
False
>>> isidentifier('re')
True
>>> isidentifier(object)
Traceback (most recent call last):
...
TypeError: expected string or buffer
"""
# test if candidate is a keyword
is_not_keyword = candidate not in keyword.kwlist
# create a pattern based on tokenize.Name
pattern_text = '^{tokenize.Name}$'.format(**globals())
# compile the pattern
pattern = re.compile(pattern_text)
# test whether the pattern matches
matches_pattern = bool(pattern.match(candidate))
# return true only if the candidate is not a keyword and the pattern matches
return is_not_keyword and matches_pattern
def test():
import unittest
import doctest
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite())
runner = unittest.TextTestRunner()
runner.run(suite)
if __name__ == '__main__':
test()
我正在使用什么:
def is_valid_keyword_arg(k):
"""
Return True if the string k can be used as the name of a valid
Python keyword argument, otherwise return False.
"""
# Don't allow python reserved words as arg names
if k in keyword.kwlist:
return False
return re.match('^' + tokenize.Name + '$', k) is not None
到目前为止提出的所有解决方案都不支持 Unicode,或者如果在 Python 3 上运行,则允许在第一个字符中包含数字。
编辑:建议的解决方案只能在 Python 2 上使用,而在 Python3 上isidentifier
应该使用。这是一个适用于任何地方的解决方案:
re.match(r'^\w+$', name, re.UNICODE) and not name[0].isdigit()
基本上,它会测试某些内容是否由(至少 1 个)字符(包括数字)组成,然后检查第一个字符是否不是数字。