生成可撤销的python代码:析取正常形式的所有函数组合

时间:2011-06-03 07:59:26

标签: python eval

(A,B,C) = (100, 200, 300)

def f1(p): return p+50 
def f2(p): return p*1.5 
def f3(p): return p*p 

vars_ = (A,B,C)
funcs_ = [f1, f2, f3]
logic_ = ["and","or"]
vol_lmt_ = [200, 300]
op_ = [">","<","="] 

我想生成eval()的断言代码字符串以测试有效性,例如:

"f1(A)>200 and f1(B)>200 and f1(C)>200"                             # True
 -^-------------^-------------^------------: funcs_
 ----^-------------^-------------^---------: vars_
 ------^-------------^-------------^-------: op_
 --------^-------------^-------------^-----: vol_lmt_        
 ------------^-------------^---------------: logic_

我的问题是:

  1. 如何基于上面的变量生成我想要的代码字符串?

  2. 如何枚举上述(A,B,C)的所有测试逻辑可能性?例如:

    "f1(A)>200 and f1(B)>200 and f1(C)>200"
    "f1(A)<300 and f2(B)=200 or f3(C)>200"

  3. 是否可以在生成代码时将函数名称替换为列表条目?

    "f(A)>200 and f1(B)>200 and f1(C)>200"

    "funcs_[0](A)>200 and funcs_[0](B)>200 and funcs_[0](C)>200"

3 个答案:

答案 0 :(得分:1)

这相当于采用外/笛卡尔积,在“var”维度上“求和”,并将其与逻辑运算符的外积相互散布。您可以使用itertools.product或仅使用正常的列表推导。 以下内容适用于任意数量的变量,函数,比较器,逻辑运算符和数字阈值。如果您选择制作更复杂的表达方式,它也很容易扩展

#!/usr/bin/python3

from pprint import pprint as pp
from itertools import *

VARS = 'XYZ'
FUNCS = range(2)
COMPARE = '><='
LOGIC = ['and', 'or']
NUMS = [200, 300]

def listJoin(iter):
    return sum(map(list,iter), [])

terms = [
    [
         'func[{func}]({var}){compare}{num}'.format(func=func, var=var, compare=compare, num=num)
         for var in VARS
    ]
    for func in FUNCS
    for compare in COMPARE
    for num in NUMS
]

def intersperse(iter, joiners):
    iter = list(iter)
    for tokens in product(*(joiners for _ in iter[:-1])):
        yield ' '.join(listJoin(zip(iter,tokens))+[iter[-1]])

formulas = listJoin(intersperse(t, LOGIC) for t in terms)

pp(formulas)

结果:

['func[0](X)>200 and func[0](Y)>200 and func[0](Z)>200',                                                                                               
 'func[0](X)>200 and func[0](Y)>200 or func[0](Z)>200',
 'func[0](X)>200 or func[0](Y)>200 and func[0](Z)>200',
 'func[0](X)>200 or func[0](Y)>200 or func[0](Z)>200',
 'func[0](X)>300 and func[0](Y)>300 and func[0](Z)>300',
 'func[0](X)>300 and func[0](Y)>300 or func[0](Z)>300',
 'func[0](X)>300 or func[0](Y)>300 and func[0](Z)>300',
 'func[0](X)>300 or func[0](Y)>300 or func[0](Z)>300',
 'func[0](X)<200 and func[0](Y)<200 and func[0](Z)<200',
 'func[0](X)<200 and func[0](Y)<200 or func[0](Z)<200',
 'func[0](X)<200 or func[0](Y)<200 and func[0](Z)<200',
 'func[0](X)<200 or func[0](Y)<200 or func[0](Z)<200',
 'func[0](X)<300 and func[0](Y)<300 and func[0](Z)<300',
 'func[0](X)<300 and func[0](Y)<300 or func[0](Z)<300',
 'func[0](X)<300 or func[0](Y)<300 and func[0](Z)<300',
 'func[0](X)<300 or func[0](Y)<300 or func[0](Z)<300',
 'func[0](X)=200 and func[0](Y)=200 and func[0](Z)=200',
 'func[0](X)=200 and func[0](Y)=200 or func[0](Z)=200',
 'func[0](X)=200 or func[0](Y)=200 and func[0](Z)=200',
 'func[0](X)=200 or func[0](Y)=200 or func[0](Z)=200',
 'func[0](X)=300 and func[0](Y)=300 and func[0](Z)=300',
 'func[0](X)=300 and func[0](Y)=300 or func[0](Z)=300',
 'func[0](X)=300 or func[0](Y)=300 and func[0](Z)=300',
 'func[0](X)=300 or func[0](Y)=300 or func[0](Z)=300',
 'func[1](X)>200 and func[1](Y)>200 and func[1](Z)>200',
 'func[1](X)>200 and func[1](Y)>200 or func[1](Z)>200',
 'func[1](X)>200 or func[1](Y)>200 and func[1](Z)>200',
 'func[1](X)>200 or func[1](Y)>200 or func[1](Z)>200',
 'func[1](X)>300 and func[1](Y)>300 and func[1](Z)>300',
 'func[1](X)>300 and func[1](Y)>300 or func[1](Z)>300',
 'func[1](X)>300 or func[1](Y)>300 and func[1](Z)>300',
 'func[1](X)>300 or func[1](Y)>300 or func[1](Z)>300',
 'func[1](X)<200 and func[1](Y)<200 and func[1](Z)<200',
 'func[1](X)<200 and func[1](Y)<200 or func[1](Z)<200',
 'func[1](X)<200 or func[1](Y)<200 and func[1](Z)<200',
 'func[1](X)<200 or func[1](Y)<200 or func[1](Z)<200',
 'func[1](X)<300 and func[1](Y)<300 and func[1](Z)<300',
 'func[1](X)<300 and func[1](Y)<300 or func[1](Z)<300',
 'func[1](X)<300 or func[1](Y)<300 and func[1](Z)<300',
 'func[1](X)<300 or func[1](Y)<300 or func[1](Z)<300',
 'func[1](X)=200 and func[1](Y)=200 and func[1](Z)=200',
 'func[1](X)=200 and func[1](Y)=200 or func[1](Z)=200',
 'func[1](X)=200 or func[1](Y)=200 and func[1](Z)=200',
 'func[1](X)=200 or func[1](Y)=200 or func[1](Z)=200',
 'func[1](X)=300 and func[1](Y)=300 and func[1](Z)=300',
 'func[1](X)=300 and func[1](Y)=300 or func[1](Z)=300',
 'func[1](X)=300 or func[1](Y)=300 and func[1](Z)=300',
 'func[1](X)=300 or func[1](Y)=300 or func[1](Z)=300']

答案 1 :(得分:0)

首先,我首先要说eval是一个坏主意。总有另一种方法可以做到。

回答你的问题:

问题1:

功能名称,

您可以使用f.func_name。

变量名称,

不是那么简单,但这应该有效,

import gc, sys

def find_names(obj):
    frame = sys._getframe()
    for frame in iter(lambda: frame.f_back, None):
        frame.f_locals
    result = []
    for referrer in gc.get_referrers(obj):
        if isinstance(referrer, dict):
            for k, v in referrer.iteritems():
                if v is obj:
                    result.append(k)
    return result

a = 97
print find_names(a)[0]

如果它返回错误的名称并不重要,因为它们的值相等。

很容易生成运算符和vol_limit。

第二个问题。

没有明显的解决方案。迭代所有这些?

第3个问题。

是的,这是可能的。 查看Decorators

答案 2 :(得分:0)

也许这可以概括你要做的事情(使用python2语法):

import itertools

arguments = ('A', 'B', 'C', 'D')
funcs_ = [f1, f2, f3, f4]
logic_ = ["and","or"]
op_ = [">","<","="]
vol_lmt_ = [200, 300]

num_func = len(funcs_)

assert num_func == len(arguments), ("The number of argument should be the same as "
                                    "the number of function.")

operands = itertools.product(["funcs_[%d]" % i for i in range(num_func)],
                             arguments,
                             op_,
                             vol_lmt_)

def comp(operands):
    templ = "{func}({arg}){op}{val}"
    for operand in operands:
        yield templ.format(func=operand[0], arg=operand[1],
                           op=operand[2], val=operand[3])

new_operands = map(comp, itertools.tee(operands, num_func))

# construct the argument to pass to itertools.product.
args = []
for operand in new_operands:
    args.append(operand)
    args.append(logic_)

args.pop() # Remove the last logic operator.

res = itertools.product(*args)

print " ".join(res.next())
# funcs_[0](A)>200 and funcs_[0](A)>200 and funcs_[0](A)>200 and funcs_[0](A)>200

...

在这种方法中,我只是用{'A','B','C'代替vars_来作弊。除此之外,我认为它应该有用。


如果您不喜欢我的作弊方式,可以通过对vars_列表和funcs_名称进行硬编码,您可以从globals字典中获取变量的名称这样:

def get_name(obj):
    """Get the name of an object (variable) from the globals dict.

    Argument:
       - obj : The variable that we want to get the name of. 

    Return:
       - A string representing the name of the object if it was found else return None.

    """

    for name, value in globals().items():
         if value is obj:
             return name