Pythonically check if a variable name is valid

后端 未结 6 2114
臣服心动
臣服心动 2020-12-29 07:37

tldr; see the final line; the rest is just preamble.


I am developing a test harness, which parses user scripts and generates a Python script which it then ru

6条回答
  •  感动是毒
    2020-12-29 07:54

    In Python 3, as above, you can simply use str.isidentifier. But in Python 2, this does not exist.

    The tokenize module has a regex for names (identifiers): tokenize.Name. But I couldn't find any documentation for it, so it may not be available everywhere. It is simply r'[a-zA-Z_]\w*'. A single $ after it will let you test strings with re.match.

    The docs say that an identifier is defined by this grammar:

    identifier ::=  (letter|"_") (letter | digit | "_")*
    letter     ::=  lowercase | uppercase
    lowercase  ::=  "a"..."z"
    uppercase  ::=  "A"..."Z"
    digit      ::=  "0"..."9"
    

    Which is equivalent to the regex above. But we should still import tokenize.Name in case this ever changes. (Which is very unlikely, but maybe in older versions of Python it was different?)

    And to filter out keywords, like pass, def and return, use keyword.iskeyword. There is one caveat: None is not a keyword in Python 2, but still can't be assigned to. (keyword.iskeyword('None') in Python 2 is False).

    So:

    import keyword
    
    if hasattr(str, 'isidentifier'):
        _isidentifier = str.isidentifier
    else:
        import re
        _fallback_pattern = '[a-zA-Z_][a-zA-Z0-9_]*'
        try:
            import tokenize
        except ImportError:
            _isidentifier = re.compile(_fallback_pattern + '$').match
        else:
            _isidentifier = re.compile(
                getattr(tokenize, 'Name', _fallback_pattern) + '$'
            ).match
    
        del _fallback_pattern
    
    
    def isname(s):
        return bool(_isidentifier(s)) and not keyword.iskeyword(s) and s != 'None'
    

提交回复
热议问题