Создание динамической функции из строки, вызываемой в Python?

#python #python-3.x

#питон #python-3.x

Вопрос:

Мой вопрос связан с пакетом расписания Дэна Бейдера. Насколько я понимаю, вы планируете вызовы функций. Это довольно просто, если вы определяете функцию в своем скрипте. Однако как насчет функций, которые создаются динамически с помощью exec() ? Есть ли какой-нибудь способ сделать их вызываемыми? Я продолжаю получать ошибки при попытке запланировать эти функции. Я признаю, что это, вероятно, не самая лучшая идея (возможно, даже не очень хорошая идея), но это только для POC, и мне все еще интересно, можно ли это сделать.

 def buildJob(lang, repType, name, file='', recipient='', server = '', db='', path=''):
    today = datetime.datetime.strftime(datetime.datetime.today(), '%m%d%Y%H%M%S')
    filePath = f"{c.path}{name}-{today}".replace('\', '/')
    filename = f'{name}-{today}.xlsx'
    funcText = f"""
def {name}():
    sql = ("{file}")
    filePath = ("{filePath}")
    engine = sa.create_engine(conString)
    dat = pd.read_sql_query(sql, engine)
    engine.dispose()
    del engine
    buildSpreadsheet(dat, filePath)
    sendSpreadsheet("{recipient}", "{filePath}.xlsx", "{filename}")
        """
 

Затем у меня есть функция для захвата funcText и exec() его. Однако, когда я передаю это в расписание, он говорит, что аргумент должен быть вызываемым.

Любая помощь будет очень признательна!

Комментарии:

1. Вы пробовали globals()['function_name'] ?

2. Почему вы делаете это вместо того, чтобы просто создать закрытие? Все «динамические» вещи кажутся простыми переменными, это не похоже на то, что фактический код функции является шаблонным.

3. В зависимости от варианта использования этот подход может обеспечить лучшие сообщения об ошибках, поскольку результирующая функция не определяется другой функцией. ( namedtuple Класс изначально был определен аналогичным образом, создавая гигантский class оператор, выполняемый exec . В конечном итоге она была повторно реализована на C для повышения производительности, но как POC, это не плохая отправная точка.)

4. Что касается globals()[‘function_name’] , когда я пытаюсь это сделать, я получаю ключевую ошибку, в которой говорится, что имя функции не найдено … и это после exec() . Тем не менее, когда я выполняю шаги вручную в терминале, функция определенно есть

5. Где это exec() происходит? Если она находится внутри функции, она будет находиться в локальной области видимости.

Ответ №1:

Вы можете получить определенные функции с locals() помощью и globals() dict s .

 # in global scope
as_str = f"""
def foo():
    print('foo')
"""
exec(as_str)
foo = globals()['foo']
foo()

# in function scope
def bar():
   as_str = f"""
def baz():
    print('baz')
""" 
   exec(as_str)
   return locals()['baz']

baz = bar()
baz()
 

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