[英]Generating evalable python code: all combinations of functions in disjunctive normal form
(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_
我的問題是:
如何根據上面的這些變量生成我想要的代碼字符串?
如何枚舉上述(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"
生成代碼時是否可以將 function 的名稱替換為列表條目?
"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"
這相當於取外積/笛卡爾積,在“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']
首先,我首先要說 eval 是一個壞主意。總有另一種方法可以做到這一點。
回答你的問題:
問題一:
Function 名稱,
您可以使用 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 很容易生成。
第二個問題。
沒有明顯的解決方案。 遍歷所有這些?
第三個問題。
對的,這是可能的。 檢查裝飾器。
也許這可以總結你正在嘗試做的事情(使用 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
...
在這種方法中,我只是通過將vars_
替換為('A','B','C')來作弊。 除此之外,我認為它應該工作。
如果您不喜歡我通過硬編碼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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.