16

Python 3 有一个名为的字符串方法str.isidentifier

我怎样才能在 Python 2.6 中获得类似的功能,而不是重写我自己的正则表达式等?

4

8 回答 8

15

tokenize 模块定义了一个名为 Name 的正则表达式

import re, tokenize, keyword
re.match(tokenize.Name + '$', somestr) and not keyword.iskeyword(somestr)
于 2010-03-30T12:37:44.480 回答
3

无效的标识符验证


该线程中的所有答案似乎都在重复验证中的错误,该错误允许无效标识符的字符串像字符串一样匹配。

其他答案中建议的正则表达式模式是从中构建的,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


引用官方 python2 正则表达式语法

$

匹配字符串的结尾或字符串结尾的换行符之前,并且在 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

于 2019-04-22T23:25:49.170 回答
2
re.match(r'[a-z_]\w*$', s, re.I)

应该做得很好。据我所知,没有任何内置方法。

于 2010-03-30T12:18:41.180 回答
1

在 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
于 2010-03-30T12:22:00.143 回答
1

到目前为止很好的答案。我会这样写。

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
于 2010-03-30T12:37:54.293 回答
1

我决定再试一次,因为有几个很好的建议。我会努力巩固它们。以下可以保存为 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()
于 2010-03-30T16:35:44.013 回答
0

我正在使用什么:

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
于 2010-03-30T13:12:51.197 回答
0

到目前为止提出的所有解决方案都不支持 Unicode,或者如果在 Python 3 上运行,则允许在第一个字符中包含数字。

编辑:建议的解决方案只能在 Python 2 上使用,而在 Python3 上isidentifier应该使用。这是一个适用于任何地方的解决方案:

re.match(r'^\w+$', name, re.UNICODE) and not name[0].isdigit()

基本上,它会测试某些内容是否由(至少 1 个)字符(包括数字)组成,然后检查第一个字符是否不是数字。

于 2016-03-06T10:13:04.840 回答