Python:在执行期间分析复杂语句

时间:2009-11-24 23:23:02

标签: python logic interpreter

我想知道在执行过程中是否有任何方法可以获得有关python语句解释的元信息。

假设这是与连接的一些单个语句的复杂语句(A,B,...是布尔函数)

if A or B and ((C or D and E) or F) or G and H:

我想知道语句的哪一部分导致语句评估为True,所以我可以用这些知识做点什么。在该示例中,将有3个可能的候选者:

A
B and ((C or D and E) or F)
G and H

在第二种情况下,我想知道是(C or D and E)F评估为True等等......

没有解析语句有什么办法吗?我可以以某种方式连接解释器或以我尚未找到的方式使用检查模块吗?我不想调试,它真的是知道这个or-chain的哪个部分在运行时触发了语句。

编辑 - 更多信息:我想要在其中使用的应用程序类型是一种分类算法,它根据其属性输入对象并输出该对象的特定类别。我需要知道哪些属性对该类别具有决定性作用 正如您可能猜到的,上面的复杂语句来自分类算法。此算法的代码是从正式的伪代码生成的,包含大约3,000个嵌套的if-elif语句,这些语句以分层方式确定类别

if obj.attr1 < 23 and (is_something(obj.attr10) or eats_spam_for_breakfast(obj)):
    return 'Category1'
elif obj.attr3 == 'Welcome Home' or count_something(obj) >= 2:
    return 'Category2a'
elif ...

除了类别本身,我需要标记对该类别具有决定性的属性,因此如果我删除所有其他属性,该对象仍将被分配到同一类别(由于语句中的or)。这些陈述可能非常长,最多可达1,000个字符,并且深度嵌套。每个对象最多可以包含200个属性。

非常感谢你的帮助!

编辑2:过去两周没有时间。感谢您提供此解决方案,它确实有效!

5 个答案:

答案 0 :(得分:3)

您可以重新编码原始代码:

if A or B and ((C or D and E) or F) or G and H:

as,say:

e = Evaluator()
if e('A or B and ((C or D and E) or F) or G and H'):

...?如果是这样,那就有希望了! - )。 Evaluator类在__call__之后将compile为其字符串参数,然后eval结果为(全局为空的真实字典,并且)为dict为{ {1}}实际上将值查找委托给其调用者的本地和全局变量(只需要一点黑魔法,但是,不要太糟糕;-)并且还要注意它的查找名称。鉴于Python的localsand的短路行为,您可以从实际查找的实际名称集推断出哪一个确定了表达式(或每个子表达式)的真值 - - 在or中,第一个真值(如果有的话)将是最后一个查找的值,而在X or Y or Z中,第一个假值将会出现。

这会有帮助吗?如果是,如果您需要编码方面的帮助,我很乐意对此进行扩展,但首先我要确认一下,X and Y and Z 的代码确实会解决你试图解决的任何问题! - )

编辑:所以这里是实施Evaluator的编码并举例说明其用法:

Evaluator

以及此处的示例运行输出:

import inspect
import random

class TracingDict(object):

  def __init__(self, loc, glob):
    self.loc = loc
    self.glob = glob
    self.vars = []

  def __getitem__(self, name):
    try: v = self.loc[name]
    except KeyError: v = self.glob[name]
    self.vars.append((name, v))
    return v


class Evaluator(object):

  def __init__(self):
    f = inspect.currentframe()
    f = inspect.getouterframes(f)[1][0]
    self.d = TracingDict(f.f_locals, f.f_globals)

  def __call__(self, expr):
    return eval(expr, {}, self.d)


def f(A, B, C, D, E):
  e = Evaluator()
  res = e('A or B and ((C or D and E) or F) or G and H')
  print 'R=%r from %s' % (res, e.d.vars)

for x in range(20):
  A, B, C, D, E, F, G, H = [random.randrange(2) for x in range(8)]
  f(A, B, C, D, E)

你可以经常看到(大约50%的时间)A是真的,这会使一切都短路。当A为假时,B计算 - 当B也为假时,则G为下一个,当B为真时,则为C.

答案 1 :(得分:1)

据我记忆,Python本身不会返回True或False:

  

重要例外:布尔值   操作orand始终返回   他们的操作之一。

Python标准库 - Truth Value Testing
因此,以下是有效的:

A = 1
B = 0
result = B or A # result == 1

答案 2 :(得分:0)

“”“我不想调试,它真的是知道这个或链的哪个部分在运行时触发了语句。”“”:你可能需要解释“调试”和“知道”之间的区别是什么哪一部分“。

你的意思是你需要在运行时告诉观察者发生了什么(为什么??)以便你可以做一些不同的事情,或者你是否意味着代码需要“知道”以便它可以做不同的东西?

在任何情况下,假设您的A,B,C等没有副作用,为什么不能简单地拆分您的or-chain并测试组件:

part1 = A
part2 = B and ((C or D and E) or F)
part3 = G and H
whodunit = "1" if part1 else "2" if part2 else "3" if part3 else "nobody"
print "Perp is", whodunit
if part1 or part2 or part3:
    do_something()

...

<强>更新

“”“调试和'知道哪个部分'之间的区别是我需要为首先评估为True的语句中使用的变量分配标志(在运行时)”“

所以你说在给定条件“A或B”的情况下,如果A为真且B为真,A获得所有荣耀(或所有责任)?我发现很难相信你描述的分类软件是基于“或”进行短路评估。您确定代码背后的意图是“A或B”而不是“B或A”吗?订单可能是随机的,还是受最初输入的变量的顺序影响?

在任何情况下,自动生成Python代码然后对其进行逆向工程似乎都是解决问题的方法。为什么不只是生成具有part1 = yadda; part2 = blah; etc性质的代码?

答案 3 :(得分:0)

Python解释器没有为您提供在运行时对表达式求值进行内省的方法。 sys.settrace()函数允许您注册为每行源代码调用的回调,但这对于您想要执行的操作来说过于粗糙。

那就是说,我已经尝试了一个疯狂的黑客来为每个执行的字节码调用函数:Python bytecode tracing

但即便如此,我也不知道如何找到执行状态,例如解释器堆栈上的值。

我认为获得所需内容的唯一方法是通过算法修改代码。你可以转换你的源代码(虽然你说你不想解析代码),或者你可以转换编译后的字节码。这也不是一件简单的事情,如果你尝试的话,我肯定会有十几个难以克服的障碍。

很抱歉让人气馁...

BTW:你对这种技术有什么应用?

答案 4 :(得分:0)

我会在大声明之前加上这样的东西(假设声明在一个类中):

for i in ("A","B","C","D","E","F","G","H"):
    print i,self.__dict__[i]