如何将Python表达式转换为字符串

时间:2015-06-17 13:19:02

标签: python

我想将用户输入表达式输出到字符串。

原因是输入表达式是用户定义的。我想输出表达式的结果,打印导致此结果的语句。

import sys
import shutil  

expression1 = sys.path
expression2 = shutil.which

def get_expression_str(expression):
    if callable(expression):
        return expression.__module__ +'.'+ expression.__name__
    else:
        raise TypeError('Could not convert expression to string')

#print(get_expression_str(expression1))
# returns : builtins.TypeError: Could not convert expression to string
#print(get_expression_str(expression2))
# returns : shutil.which

#print(str(expression1))
#results in a list like ['/home/bernard/clones/it-should-work/unit_test', ...  ,'/usr/lib/python3/dist-packages']

#print(repr(expression1))
#results in a list like ['/home/bernard/clones/it-should-work/unit_test', ...  ,'/usr/lib/python3/dist-packages']

我查看了Python检查模块,但即使是

inspect.iscode(sys.path)

返回False

对于那些想知道为什么它是使用functools.partial解析为表达式的字符串的反向的人,请参阅parse statement string

背景

程序应该有效。应该,但并非总是如此。因为程序需要特定的资源,操作系统,操作系统版本,其他包,文件等。每个程序都需要不同的要求(资源)才能正常运行。 无法预测需要哪种具体要求。系统最清楚哪些资源是可用的,哪些资源不可用。因此,不是手动检查所有设置和配置,而是让帮助程序为您执行此操作。

因此,程序的用户或开发人员将语句与语句一起指定如何检索此信息:表达式。哪个可以使用eval执行。可以。就像在StackOverflow上提到的那样,eval是邪恶的。 使用黑名单很难确保使用eval,请参阅:http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html 使用SO的多个提示我使用带有字符串的namedtuple来比较用户输入字符串和函数。

白名单比黑名单更好。仅当解析的表达式字符串与“bare_expression”匹配时,才返回表达式。 此白名单包含有关如何处理f.e的更多信息。 “unit_of_measurement”。它解释了什么和为什么,但这是必要的。 namedtuples的列表不仅仅是一个白名单,而是定义的:

Expr_UOfM = collections.namedtuple('Expr_UOfM', ['bare_expression', 'keylist', 'function', 'unit_of_measurement', 'attrlist'])

匹配(非常有限)列表的namedtuple:

Exp_list = [Expr_UOfM('sys.path', '' , sys.path, un.STR, []),
            Expr_UOfM('shutil.which', '', shutil.which, None, [])] 

此列表可能很长,内容对于进一步正确处理至关重要。注意第一和第三个字段非常相似。应该有一个参考点,但对我来说,这是不可能的。请注意字符串:'sys.path'等于用户输入的(一部分),表达式:sys.path是namedtuple列表的一部分。良好的分离,限制可能的滥用。 如果字符串和表达式不是100%相同,则可能会发生奇怪的行为,这很难调试。 所以它想要使用get_expression_str函数检查第一个和第三个字段是否相同。只是为了完全的稳健性 该程序。

我使用Python 3.4

2 个答案:

答案 0 :(得分:0)

为什么不使用eval

>>> exp1 = "sys.path"
>>> exp2 = "[x*x for x in [1,2,3]]"
>>> eval(exp1)
['', 'C:\\Python27\\lib\\site-packages\\setuptools-0.6c11-py2.7.egg', 'C:\\Pytho
n27\\lib\\site-packages\\pip-1.1-py2.7.egg', 'C:\\Python27\\lib\\site-packages\\
django_celery-3.1.1-py2.7.egg', 'C:\\Python27\\lib\\site-packages\\south-0.8.4-p
y2.7.egg', 'C:\\Windows\\system32\\python27.zip', 'C:\\Python27\\DLLs', 'C:\\Pyt
hon27\\lib', 'C:\\Python27\\lib\\plat-win', 'C:\\Python27\\lib\\lib-tk', 'C:\\Py
thon27', 'C:\\Python27\\lib\\site-packages', 'C:\\Python27\\lib\\site-packages\\
PIL']
>>> eval(exp2)
[1, 4, 9]

答案 1 :(得分:0)

您可以使用inspect.getsource()并将表达式包装在lambda中。然后,您可以使用此函数获取表达式:

def lambda_to_expr_str(lambda_fn):
    """c.f. https://stackoverflow.com/a/52615415/134077"""
    if not lambda_fn.__name__ == "<lambda>":
        raise ValueError('Tried to convert non-lambda expression to string')
    else:
        lambda_str = inspect.getsource(lambda_fn).strip()
        expression_start = lambda_str.index(':') + 1
        expression_str = lambda_str[expression_start:].strip()
        if expression_str.endswith(')') and '(' not in expression_str:
            # i.e. l = lambda_to_expr_str(lambda x: x + 1) => x + 1)
            expression_str = expression_str[:-1]
        return expression_str

用法:

$ lambda_to_expr_str(lambda: sys.executable)
> 'sys.executable'

OR

$ f = lambda: sys.executable
$ lambda_to_expr_str(f)
> 'sys.executable'

然后与

进行评估
$ eval(lambda_to_expr_str(lambda: sys.executable))
> '/usr/bin/python3.5'

请注意,您可以使用这种方法获取参数,并将其传递给eval的locals参数。

$ l = lambda_to_expr_str(lambda x: x + 1)  #  now l == 'x + 1'
$ eval(l, None, {'x': 1})
> 2

这里是龙。这种方法有很多病理情况:

$ l, z = lambda_to_expr_str(lambda x: x + 1), 1234
$ l
> 'x + 1), 1234'

这是因为inspect.getsource获得了声明lambda的全部代码行。获取使用def声明的函数的源将避免此问题,但是将函数主体传递给eval是不可能的,因为可能会有副作用,即设置变量等。Lambda会产生副作用在Python 2中也是如此,因此在Python-3之前的土地上还有更多的龙。