用Python来检查变量名是否有效

时间:2016-03-31 10:31:07

标签: python

tldr;看到最后一行;其余的只是序言。

我正在开发一个测试工具,它解析用户脚本并生成一个随后运行的Python脚本。这个想法是让非技术人员能够编写高级测试脚本。

我已经介绍了变量的概念,因此用户可以在他的脚本中使用LET关键字。例如。 LET X = 42,我只是扩展到X = 42。然后,他们可以在脚本中使用X - RELEASE CONNECTION X

但如果有人写LET 2 = 3怎么办?那会产生无效的Python。

如果我在变量X中有variableName,那么如何检查variableName是否是有效的Python变量?

6 个答案:

答案 0 :(得分:31)

在Python 3中,您可以使用str.isidentifier()来测试给定字符串是否是有效的Python标识符/名称。

>>> 'X'.isidentifier()
True
>>> 'X123'.isidentifier()
True
>>> '2'.isidentifier()
False
>>> 'while'.isidentifier()
True

最后一个示例显示您还应检查变量名称是否与Python关键字冲突:

>>> from keyword import iskeyword
>>> iskeyword('X')
False
>>> iskeyword('while')
True

所以你可以把它放在一个函数中:

from keyword import iskeyword

def is_valid_variable_name(name):
    return name.isidentifier() and not iskeyword(name)

另一个在Python 2和3中有效的选项是使用ast模块:

from ast import parse

def is_valid_variable_name(name):
    try:
        parse('{} = None'.format(name))
        return True
    except SyntaxError, ValueError, TypeError:
        return False

>>> is_valid_variable_name('X')
True
>>> is_valid_variable_name('123')
False
>>> is_valid_variable_name('for')
False
>>> is_valid_variable_name('')
False
>>> is_valid_variable_name(42)
False

这将解析赋值语句而不实际执行它。它将获取无效标识符以及尝试分配给关键字。在上面的代码中,None是一个分配给给定名称的任意值 - 它可以是RHS的任何有效表达式。

答案 1 :(得分:2)

您可以使用异常处理并实际捕获NameErrorSyntaxError。在try/except块内测试它,并告知用户是否存在无效输入。

答案 2 :(得分:1)

您可以尝试测试作业并查看它是否会引发SyntaxError

>>> 2fg = 5
  File "<stdin>", line 1
    2fg = 5
      ^
SyntaxError: invalid syntax

答案 3 :(得分:1)

在Python 3中,如上所述,您只需使用str.isidentifier即可。但是在Python 2中,这不存在。

tokenize模块具有名称(标识符)的正则表达式:tokenize.Name。但我无法找到任何文档,因此它可能无法在任何地方使用。它只是r'[a-zA-Z_]\w*'。一个$后面的re.match将允许您使用identifier ::= (letter|"_") (letter | digit | "_")* letter ::= lowercase | uppercase lowercase ::= "a"..."z" uppercase ::= "A"..."Z" digit ::= "0"..."9" 测试字符串。

docs表示该语法定义了一个标识符:

tokenize.Name

这相当于上面的正则表达式。但是,如果这种情况发生变化,我们仍应导入pass。 (这是非常不可能的,但可能在旧版本的Python中有所不同?)

要过滤掉defreturnkeyword.iskeyword等关键字,请使用None。有一点需要注意:keyword.iskeyword('None')不是Python 2中的关键字,但仍然无法分配。 (Python 2中的Falseimport 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' )。

所以:

df['G']=df.groupby(level='Date').cumcount()
df
Out[125]: 
                 string  number  G
Date                              
2017-01-16  stringvalue      90  0
2017-01-16  stringvalue     912  1
2017-01-16  stringvalue      29  2
2017-01-17  stringvalue     883  0
2017-01-17  stringvalue     223  1
2017-01-17  stringvalue     211  2
2015-04-30  stringvalue     908  0
2017-03-30  stringvalue     348  0

df.sort_values('G').sort_index().drop('G',1)
Out[124]: 
                 string  number
Date                           
2015-04-30  stringvalue     908
2017-01-16  stringvalue      90
2017-01-16  stringvalue     912
2017-01-16  stringvalue      29
2017-01-17  stringvalue     883
2017-01-17  stringvalue     223
2017-01-17  stringvalue     211
2017-03-30  stringvalue     348

答案 4 :(得分:1)

你可以让Python(在我所知的今天使用的任何版本上工作)按照它们通常在内部的方式进行检查,并捕获异常:

def _dummy_function_taking_kwargs(**_):
    pass

try:
    _dummy_function_taking_kwargs(**{my_variable: None})
    # if the above line didn't raise and we get here,
    # the keyword/variable name was valid.
    # You could also replace the external dummy function
    # with an inline lambda function.
except TypeError:
    # If we get here, it wasn't.

值得注意的是,每当TypeError经历关键字参数扩展并且其密钥不是有效函数参数时,dict始终会被提升,只要{{1}使用无效键构造文字。

优于已接受的答案的是,它在<3>和<3> 2 之间 兼容,而不像dict / {{那样脆弱1}}方法(将ast.parse之类的字符串视为有效)。

我还没有彻底审核这个解决方案,或者写了假设测试来模糊它,所以可能会有一些极端情况,但它似乎通常适用于Python 3.7,3.6,2.7和2.5(不是任何人< em>应该现在使用2.5,但它仍然是野外的,你可能是为数不多的可怜的草皮之一,不得不编写与2.6 / 2.5一起工作的代码。

答案 5 :(得分:0)

我认为您不需要与python本身完全相同的命名语法。 宁愿选择一个简单的正则表达式:

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Controls:ItemView />
        </DataTemplate>
    </ItemsControl.ItemTemplate>

    <ItemsControl.Template>
        <ControlTemplate TargetType="ItemsControl">
            <StackPanel>
                <ItemsPresenter />
                <Button Content="Add Item"  Click="AddItem_Click"/>
            </StackPanel>
        </ControlTemplate>
    </ItemsControl.Template>

</ItemsControl>

确保它是字母数字,然后添加一个前缀以远离python自己的语法。所以非技术用户的声明:

\w+

应该在解析之后成为:

LET return = 12