簡體   English   中英

生成可評估的 python 代碼:析取范式的所有函數組合

[英]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_

我的問題是:

  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. 生成代碼時是否可以將 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM