Как динамически изменить сигнатуру функции - PullRequest
0 голосов
/ 11 июня 2019

Я пишу фреймворк на Python. Когда пользователь объявляет функцию, он делает:

def foo(row, fetch=stuff, query=otherStuff)

def bar(row, query=stuff)

def bar2(row)

Когда сервер видит запрос = значение, он выполняет функцию с аргументом запроса в зависимости от значения. Таким образом, функция получает доступ к результату чего-либо, что сделано бэкендом в своей области видимости.

В настоящее время я строю свои аргументы каждый раз, проверяя, имеют ли значение «запрос», «выборка» и другие элементы «Нет», и запуская его с набором аргументов, которые точно соответствуют запросам пользователя. В противном случае я получил ошибку «получил неожиданный аргумент ключевого слова». Это код в бэкэнде:

#fetch and query is something computed by the backend
if fetch= None and query==None:
    userfunction(row)
elif fetch==None:
    userunction (row, query=query)
elif query == None:
    userfunction (row, fetch=fetch)
else:
    userfunction (row,fetch=fetch,query=query)

Это не хорошо; для каждой дополнительной «услуги», которую предлагает бэкэнд, мне нужно написать все комбинации с предыдущими.

Вместо этого я хотел бы прежде всего взять функцию и вручную добавить именованный параметр, прежде чем выполнять его, удаляя весь ненужный код, который выполняет эти проверки. Тогда пользователь будет просто использовать то, что ему действительно нужно.

Я не хочу, чтобы пользователю приходилось изменять функцию, добавляя ненужные ему вещи (и при этом я не хочу, чтобы они указывали kwarg каждый раз).

Так что я хотел бы привести пример, если это выполнимо, функцию addNamedVar(name, function), которая добавляет переменную name к функции function.

Я хочу сделать это таким образом, потому что пользовательские функции вызываются много раз, а это означает, что это побудит меня, например, создать dict для именованного var функции (с проверкой) и затем использовать **dict. Я действительно хотел бы просто изменить функцию один раз, чтобы избежать каких-либо накладных расходов.

1 Ответ

0 голосов
/ 28 июня 2019

Это действительно выполнимо в AST, и это то, что я собираюсь сделать, потому что это решение лучше подойдет для моего случая использования. Однако вы могли бы сделать то, что я просил, проще, используя подход клонирования функций, подобный фрагменту кода, который я показываю. Обратите внимание, что этот код возвращает одинаковые функции с разными значениями по умолчанию. Вы можете использовать этот код в качестве примера, чтобы делать все, что вы хотите. Это работает для python3

def copyTransform(f, name, **args):
    signature=inspect.signature(f)
    params= list(signature.parameters)
    numberOfParam= len(params)
    numberOfDefault= len(f.__defaults__)
    listTuple= list(f.__defaults__)

    for key,val in args.items():
        toChangeIndex = params.index(key, numberOfDefault)
        if toChangeIndex:
            listTuple[toChangeIndex- numberOfDefault]=val

    newTuple= tuple(listTuple)
    oldCode=f.__code__

    newCode= types.CodeType(
        oldCode.co_argcount,             #   integer
        oldCode.co_kwonlyargcount,       #   integer
        oldCode.co_nlocals,              #   integer
        oldCode.co_stacksize,            #   integer
        oldCode.co_flags,                #   integer
        oldCode.co_code,                 #   bytes
        oldCode.co_consts,               #   tuple
        oldCode.co_names,                #   tuple
        oldCode.co_varnames,             #   tuple
        oldCode.co_filename,             #   string
        name,                            #   string
        oldCode.co_firstlineno,          #   integer
        oldCode.co_lnotab,               #   bytes
        oldCode.co_freevars,             #   tuple
        oldCode.co_cellvars              #   tuple
        )

    newFunction=types.FunctionType(newCode, f.__globals__, name, newTuple, f.__closure__)
    newFunction.__qualname__=name #also needed for serialization

Вам нужно сделать эту странную вещь с именами, если вы хотите выбрать функцию клона.

...