2016-03-09 2 views
1

У меня есть скрипт 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.

+0

Почему бы не скомпилировать ямль в модуль python и не импортировать его? –

+0

@KurtStutsman Это отличается от yaml.load()? У меня уже есть файл yaml, импортированный как питон. –

+0

Да. Вы обрабатываете файл и записываете модуль python в файл .py. Затем вы можете использовать функцию import() 'для ее загрузки. –

ответ

0

Вот упрощенный пример того, что я имею в виду, используя ваш ввод. Я жёстко, но вы можете легко создать подобный файл модуля с помощью синтаксического анализатора:

def makeModule(**kwargs): 
    print repr(kwargs) 

    module_filename = 'generated_module.py' 
    with open(module_filename, 'w') as module_file: 
     module_file.write('''\ 
from math import * 

def func(voltage, current): 
    def f(voltage): 
     return exp({a1} * voltage) 

    return {a0}*voltage+{b}*current+f(voltage) 
'''.format(**kwargs)) 

    module_name = module_filename.replace('.py', '') 
    module = __import__(module_name) 
    return module.func 

def main(): 
    func = makeModule(a0=-1.23, b=0.772, a1=-6.32) 
    print 'Result:', func(2, 3) 

if __name__ == '__main__': 
    main() 

Он работает генерируя файл с именем generated_module.py, а затем с помощью встроенной функции __import__ импортировать его как модуль, который хранится в переменной module. Как и любой другой модуль, вы можете получить доступ к именам, определенным в нем, а именно func.

+0

Thx! Это было полезно. –

+0

Есть ли способ «генерировать» функцию без файла; т.е. загрузить функцию в интерпретатор? Я работаю над проектом, который использует файлы JSON для описания вызываемых объектов. В JSON-файлах есть встроенная в них логика «python». – Julien

Смежные вопросы