2

我有一些条件/比较存储为字符串。我如何检查这些条件?下面给出一个非常简单的例子。我想要条件作为字符串,因为我想打印它们以防它们失败。

我想我需要为此制作一个解析器,但为一件小事制作一个完整的 Python 解析器将是一个非常愚蠢的想法。有什么想法可以做什么?

def rev_num(num):
    if num < 0:
        return -int(str(-num)[::-1])
    else:
        return int(str(num)[::-1])

conditions = ['rev_num(-34) != -43', 'rev_num(34) != 43']

for i in conditions:
     if something-needs-to-come-here(i):
           print(i)

我知道这是一个奇怪的想法,但如果可以的话,请附上。


我给 user2357112 造成了一些困惑。他指出我正在尝试做的事情叫做unit-testing. 感谢那。

为避免进一步混淆,我将添加我正在尝试改进的代码。我要做的更改是在函数中打印条件,correctness使其返回False

def rev_num(num):
    if num < 0:
        return -int(str(-num)[::-1])
    else:
        return int(str(num)[::-1])

if __name__ == "__main__":
    from timeit import Timer
    import random

    def correctness(f):
        print("Correctness Test")
        if f(-34) != -43 or f(34) != 43:
            return False
        print('Correct')
        print('-----------')
        return True

    def timing(f, times):
        def test1(f):
            f(random.randint(1, 1000))
        def test2(f):
            f(random.randint(100000, 1000000))

        print("Timing Test")
        print(Timer(lambda: test1(f)).timeit(number = times))
        print(Timer(lambda: test2(f)).timeit(number = times))
        print('-----------')

    def tests(f,times):
        print(f.__name__)
        print('-----------')
        if correctness(f) is True:
            timing(f, times)

    repeat = 100000
    tests(rev_num, repeat)
4

7 回答 7

3

你可以使用eval,但我不建议这样做。如果您已经知道要对 执行多次调用rev_num(x) != y,只需创建一个辅助函数并使用元组列表来存储参数:

def check_condition(x, y):
    return rev_num(x) != y

conditions = [(-34, -43), (34, 43)]

for i in conditions:
    if check_condition(*i):
        print('rev_num({}) != {}'.format(*i))
于 2013-07-19T17:37:15.033 回答
1

可以使用eval(cond_string)

for i in conditions:
     if eval(i):
           print(i)

编辑:是的,正如一些人指出的那样,eval如果您不能绝对确定要评估的字符串的内容,可能会很危险。出于这个原因,使用eval通常被视为不好的一般做法,即使它可能是实现您在这里目标的最简单方法。

如果您的目的是为了代码维护目的执行完整性检查,您还可以查看unittest模块

于 2013-07-19T17:36:40.927 回答
1

如果要打印调试条件,请改为引发异常。这将为您提供完整的堆栈跟踪,包括引发异常的行号。它提供了您想要的所有好处,没有任何缺点。

如果它不是致命的,请发出警告。这仍然会给你一个堆栈跟踪和一个行号,但它不会停止程序,你可以在发货时关闭警告。

如果您想向用户显示条件?你没有。该错误消息对用户无用。显示更多信息。

于 2013-07-19T17:43:55.440 回答
1

虽然eval有用,但它不是我的首选(如果您的条件之一是 会发生什么blow_up()?那eval(blow_up())将是灾难性的)

相反,我会推荐一个稍微重一些但功能强大且可扩展的解决方案:
首先,将您的条件表示为元组(function, parameter, comparison operator, comparison value)

conditions = [(rev_num, -34, opoerator.ne, 43), (rev_num, 34, opoerator.ne, 43)]

然后可以按如下方式使用:

for func, param, op, num in conditions:
    op(func(param), num):
        print("%s(%s) %s %s" %(func.__name__, param, op.__name__, val))

这种表示的好处在于,您可以很容易地对其进行扩展,以适应任何比较器、任何函数以及该函数的任何数量的参数:

conditions = [(my_func, (-34, 35), opoerator.ne, 43)]
for func, params, comparator, val in conditions:
    if op(func(*params), val):
        print(something)

我应该注意:这里的一个缺点'ne'是打印出来而不是'!=',这可能是需要的。如果这仅用于调试目的,那么您可能不会太在意。但是,如果这是功能完整的一部分,那么您可能需要dict在时髦的operator.x函数名称和您希望它们的字符串表示形式之间创建一个映射

希望这可以帮助

于 2013-07-19T17:51:36.703 回答
1

为无法使用 eval 的人回答:使用名为 sympy 的 Python 数学库。要解决您的示例:

conditions = ['rev_num(-34) != -43', 'rev_num(34) != 43']

你可以这样写:

condition = [str(rev_num(-34)) + ' != -43), str(rev_num(34)) + ' != 43]

然后使用 sympy 你可以评估这些陈述是对还是错:

for s in conditions:
  if not sympify(s)
    print(s) #prints if the condition is false

sympy 的优点在于,您可以使用和/或编写为 | 链接条件。对于或和 & 对于和。所以你也可以这样做:

conditions = '(' + str(rev_num(-34)) + ' != -43) & (' + str(rev_num(34)) + ' != 43)'  
sympify(conditions) #returns true or false depending on the conditions

您还可以将符号/变量用于 sympy 并在您的条件中使用它们。(x != -43) & (y != 43) 看起来更好一些。

于 2015-03-20T13:04:10.453 回答
0

如果这是您代码的性能关键部分,并且如果这组条件是固定的(不是用户给定的),我会重复工作并通常对这些条件进行编码并在它们失败时记录它们相应的字符串表示。

于 2013-07-19T17:37:00.440 回答
0

ast.literal_eval http://docs.python.org/2/library/ast.html?highlight=literal#ast.literal_eval

如果这对您不起作用,那么该ast模块是 python 的完整解析基础结构。

如果您的条件都有特定的形式,您也可以单独存储组件。

于 2013-07-19T17:40:19.183 回答