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

如何将Python表达式转换为字符串,python,Python,我想将用户输入表达式输出到字符串 原因是输入表达式是用户定义的。我想输出表达式的结果,并打印导致此结果的语句 import sys import shutil expression1 = sys.path expression2 = shutil.which def get_expression_str(expression): if callable(expression): return expression.__module__ +'.'+ expressi

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

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

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模块,但甚至

inspect.iscode(sys.path)
返回False

对于那些想知道为什么它与使用functools.partial解析为表达式的字符串相反的人,请参见

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

因此,程序的用户或开发人员可以指定他的需求以及如何检索这些信息的语句:表达式。可以使用eval执行。能够就像StackOverflow eval上提到的那样,eval是邪恶的。 使用黑名单很难确保eval的安全,请参阅: 使用多个提示,所以我使用一个带字符串的namedtuple与用户输入字符串和一个函数进行比较

白名单比黑名单好。仅当解析的表达式字符串与“bare_expression”匹配时,才会返回表达式。 此白名单包含如何处理f.e.“测量单位”的更多信息。要解释什么和为什么要这样做,这是需要的。namedtuples列表不仅仅是一个白名单,而且定义如下:

Expr_UOfM = collections.namedtuple('Expr_UOfM', ['bare_expression', 'keylist', 'function', 'unit_of_measurement', 'attrlist'])
与(非常有限的)列表匹配的namedtuple:

此列表可能很长,内容对于进一步正确处理至关重要。注意,第一个和第三个字段非常相似。应该有一个单一的参考点,但对我来说,这在此刻是不可能的。请注意,字符串“sys.path”等于(部分)用户输入,表达式:sys.path是namedtuple列表的一部分。良好的分离,限制可能的滥用。 如果字符串和表达式不是100%相同,则可能会出现很难调试的奇怪行为。 因此,它需要使用get_expression_str函数检查第一个字段和第三个字段是否相同。只是为了保证系统的健壮性 节目


我使用Python 3.4,为什么不使用
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]

为什么不使用
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]

您可以使用
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

然后用

$ 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之前的版本中会有更多的龙。

您可以使用
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

然后用

$ 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之前的版本中会有更多的龙。

try
str(expression)
I这样做了,请查看底部的4e行,但没有成功。那么用户的输入在哪里呢?你不是从一个字符串(例如,
'sys.path'
)开始吗?@jornsharpe,是的,我是。但它存储在一个名为function(part)的tuple中,因此可以重用。我想打印(使用XML)NamedTupleVerbose的内容,以检查可能出现的错误。您能提供更多的上下文吗?就目前情况而言,根本不清楚您想做什么以及为什么。请尝试
str(expression)
我这样做了,请查看底部的4e行,但没有成功。那么用户的输入在哪里?你不是从一个字符串(例如,
'sys.path'
)开始吗?@jornsharpe,是的,我是。但它存储在一个名为function(part)的tuple中,因此可以重用。我想打印(使用XML)NamedTupleVerbose的内容,以检查可能出现的错误。您能提供更多的上下文吗?就目前情况而言,根本不清楚您想做什么以及为什么。请尝试
str(expression)
我这样做了,请查看底部的4e行,但没有成功。那么用户的输入在哪里?你不是从一个字符串(例如,
'sys.path'
)开始吗?@jornsharpe,是的,我是。但它存储在一个名为function(part)的tuple中,因此可以重用。我想打印(使用XML)NamedTupleVerbose的内容,以检查可能出现的错误。您能提供更多的上下文吗?就目前而言,根本不清楚你想做什么以及为什么。我的意思是eval的反面。eval输入是字符串,输出是表达式。我想输入一个表达式,输出一个字符串。在你的例子中[1,4,9]返回exp2,在我看来这是不可能的。顺便说一下,eval使用起来非常危险,这就是我使用functools.partial的原因。这似乎是不可能的。我不知道使用场景是什么