У меня есть скрипт yaml, который мы используем для указания функций. Файл yaml анализирует в словаре (фактически, вложенные словари), который я хочу использовать для построения функций, описанных в этом файле yaml. Вот пример записи YAML:Динамическое создание вложенных функций из сценария yaml
Resistance:
arguments:
voltage: "V"
current: "A"
parameters:
a: -1.23
b: 0.772
format: "{a}*voltage+{b}*current+f(voltage)"
subfunctions:
f:
arguments:
voltage: "V"
parameters:
a: -6.32
format: "exp({a}*voltage)"
Теперь, что нужно сделать, это разобрать этот файл, а затем создать пространство имен, так что в конце концов, я могу связать переменную с именем «Сопротивление» к закрытию или лямбда, что отражает указанную выше функцию (с вложенной подфункцией f).
Моей стратегией было пойти «снизу вверх» с использованием рекурсивного алгоритма. Вот мой код:
def evaluateSimpleFunction(entry):
functionString = entry['format']
functionArgs = []
Params = []
if "arguments" in entry and entry["arguments"] != None:
functionArgs = entry['arguments'].keys()
if "parameters" in entry and entry["parameters"] != None:
Params = entry['parameters']
formatString = ""
for param in Params:
formatString += str(param)+"="+str(Params[param])+","
functionString = eval("functionString.format("+formatString+")")
lambdaString = ""
for arg in functionArgs:
lambdaString += str(arg)+","
return eval("lambda " + lambdaString + ":" + functionString)
def recursiveLoader(entry):
if "subfunctions" in entry:
subfunctions = entry['subfunctions']
bindingString = ""
for subFunc in subfunctions:
bindingString +=str(subFunc)+"=[];"
exec(bindingString)
for subFunc in subfunctions:
exec(str(subFunc)+"= recursiveLoader(subfunctions[subFunc])")
return lambda : evaluateSimpleFunction(entry)
else:
return lambda : evaluateSimpleFunction(entry)
import yaml,os, math
os.chdir(r"C:\Users\212544808\Desktop\PySim\xferdb")
keyFields = ["Resistance","OCV"]
containerKeys = ["_internalResistance","_OCV"]
functionContainer = {}
with open("LGJP1.yml",'r') as modelFile:
parsedModelFile = yaml.load(modelFile)
#for funcKey,containerKey in zip(keyFields,containerKeys):
entry = parsedModelFile["capacityDegrade"]
g = recursiveLoader(entry)
Теперь, как он стоит, я получаю сообщение об ошибке, потому что я использую безоговорочную Exec с вложенной функцией.
Однако я не хочу прибегать к глобальным переменным, потому что я буду использовать этот процесс для нескольких функций и поэтому перезапишу любые глобальные переменные, которые я использую.
Я надеюсь на предложения о том, как построить вложенные функции алгоритмически из внешнего файла конфигурации, такого как файл yaml - exec, похоже, не подходит.
BTW: Я использую Python 2.7
UPPDATE
Другой, более надежный вариант может быть использовать экземпляр глобального класса, чтобы создать пространство имен для каждой функции. Например:
class Namespace(): pass
namespace_1 = Namespace()
#assume that the function "exponent" has arguments X, Y and body "Q(X*Y)",
#where "Q" has body "x**2+3*y"
exec("namespace_1.exponent = lambda X,Y: Q(X*Y)")
exec("namespace_1.Q = lambda x,y: x**2+3*y")
Преимущество этого подхода заключается в том, что я могу затем перебрать член класса для конкретной функции, чтобы создать строку кода одного источника, который я могу передать в «Eval», чтобы получить окончательные функция.
Я делаю все это, потому что я не нашел надежного способа создания вложенных замыканий с использованием eval и exec.
Почему бы не скомпилировать ямль в модуль python и не импортировать его? –
@KurtStutsman Это отличается от yaml.load()? У меня уже есть файл yaml, импортированный как питон. –
Да. Вы обрабатываете файл и записываете модуль python в файл .py. Затем вы можете использовать функцию import() 'для ее загрузки. –