Установить подпись функции в Python

Предположим, что у меня есть общая функция f. Я хочу программно создать функцию f2, которая ведет себя так же, как f, но имеет настраиваемую подпись.

Более детально

  • Как получить имя функции в виде строки в Python?
  • Как преобразовать строку в функцию в python?
  • создание объекта функции из строки
  • Разрешенные символы в именах функций Python
  • Boost.Python: функции Wrap для выпуска GIL
  • Понимание стиля вызова by-by-Python передаваемых аргументов функции
  • Учитывая список l и и словарь d, я хочу иметь возможность:

    • Задайте аргументы без ключевого слова f2 для строк в l
    • Задайте аргументы ключевого слова f2 клавишам в d, а значения по умолчанию – значениям d

    то есть. Предположим, что мы имеем

    l=["x", "y"] d={"opt":None} def f(*args, **kwargs): #My code 

    Тогда мне нужна функция с сигнатурой:

     def f2(x, y, opt=None): #My code 

    Конкретный вариант использования

    Это просто упрощенная версия моего конкретного варианта использования. Я даю это только как пример.

    Мой фактический прецедент (упрощенный) выглядит следующим образом. У нас есть общая инициативная функция:

     def generic_init(self,*args,**kwargs): """Function to initiate a generic object""" for name, arg in zip(self.__init_args__,args): setattr(self, name, arg) for name, default in self.__init_kw_args__.items(): if name in kwargs: setattr(self, name, kwargs[name]) else: setattr(self, name, default) 

    Мы хотим использовать эту функцию в нескольких классах. В частности, мы хотим создать функцию init, которая ведет себя как generic_init, но имеет подпись, определенную некоторыми переменными класса во время создания :

     class my_class: __init_args__=["x", "y"] __kw_init_args__={"my_opt": None} __init__=create_initiation_function(my_class, generic_init) setattr(myclass, "__init__", __init__) 

    Мы хотим создать create_initiation_function для создания новой функции с сигнатурой, определенной с помощью init_args и kw_init_args . Можно ли написать create_initiation_function?

    Пожалуйста, обратите внимание:

    • Если бы я просто хотел улучшить помощь, я мог бы установить doc .
    • Мы хотим установить подпись функции при создании. После этого его не нужно менять.
    • Вместо создания функции типа generic_init, но с другой подписью мы могли бы создать новую функцию с нужной сигнатурой, которая просто вызывает generic_init
    • Мы хотим определить create_initiation_function. Мы не хотим вручную указывать новую функцию!

    Связанный

    • Сохранение подписей украшенных функций : это то, как сохранить подпись при декорировании функции. Нам нужно иметь возможность установить подпись на произвольное значение

  • Добавьте знак процента в столбец dataframe в Python
  • Многопроцессорный пул Python. Как выйти из сценария, когда один из рабочих процессов определяет, что больше работы не требуется?
  • Разделите изображение на 5x5 блоков в python и вычислите гистограмму для каждого блока
  • API поиска Amazon
  • Как разрешить Django AllAuth - «Соединение прервано.», Ошибка (13, «Отказано в разрешении») Ошибка?
  • Есть ли способ передать «stdin» в качестве аргумента для другого процесса в python?
  • 6 Solutions collect form web for “Установить подпись функции в Python”

    Для вашего usecase, должен иметь docstring в классе / функции, который будет отображаться в help () в порядке и может быть установлен программно (func .__ doc__ = "stuff").

    Я не вижу способа установить реальную подпись. Я бы подумал, что модуль functools сделал бы это, если бы это выполнимо, но это не так, по крайней мере, в py2.5 и py2.6.

    Вы также можете создать исключение TypeError, если у вас плохой ввод.

    Хм, если вы не против быть по-настоящему мерзким, вы можете использовать compile () / eval () для этого. Если ваша желаемая подпись указана arglist = ["foo", "bar", "baz"], а ваша фактическая функция f (* args, ** kwargs), вы можете управлять:

     argstr = ", ".join(arglist) fakefunc = "def func(%s):\n return real_func(%s)\n" % (argstr, argstr) fakefunc_code = compile(fakefunc, "fakesource", "exec") fakeglobals = {} eval(fakefunc_code, {"real_func": f}, fakeglobals) f_with_good_sig = fakeglobals["func"] help(f) # f(*args, **kwargs) help(f_with_good_sig) # func(foo, bar, baz) 

    Изменение docstring и func_name должно дать вам полное решение. Но, э-э …

    Из PEP-0362 на самом деле существует способ установить подпись в py3.3 +, используя атрибут fn.__signature__ :

     def shared_vars(*shared_args): """Decorator factory that defines shared variables that are passed to every invocation of the function""" def decorator(f): @wraps(f) def wrapper(*args, **kwargs): full_args = shared_args + args return f(*full_args, **kwargs) # Override signature sig = signature(f) sig = sig.replace(parameters=tuple(sig.parameters.values())[1:]) wrapper.__signature__ = sig return wrapper return decorator 

    Затем:

     >>> @shared_vars({"myvar": "myval"}) >>> def example(_state, a, b, c): >>> return _state, a, b, c >>> example(1,2,3) ({'myvar': 'myval'}, 1, 2, 3) >>> str(signature(example)) '(a, b, c)' 

    Примечание: PEP не совсем прав; Signature.replace переместил params из позиционного arg в kw-only arg.

    Мы хотим, чтобы create_initiation_function менял подпись

    Пожалуйста, не делайте этого.

    Мы хотим использовать эту функцию в нескольких классах

    Используйте обычное наследование.

    Нет никакой ценности в том, что подпись «изменена» во время выполнения.

    Вы создаете кошмар для обслуживания. Никто больше никогда не захочет понять, что вы делаете. Они просто уничтожат его и заменят его наследованием.

    Сделайте это вместо этого. Это просто и понятно и делает ваш общий init доступным во всех подклассах очевидным, простым, путинским способом.

     class Super( object ): def __init__( self, *args, **kwargs ): # the generic __init__ that we want every subclass to use class SomeSubClass( Super ): def __init__( self, this, that, **kwdefaults ): super( SomeSubClass, self ).__init__( this, that, **kwdefaults ) class AnotherSubClass( Super ): def __init__( self, x, y, **kwdefaults ): super( AnotherSubClass, self ).__init__( x, y, **kwdefaults ) 

    Вы не можете сделать это с помощью живого кода.

    То есть вам кажется, что вы хотите принять реальную, живую функцию, которая выглядит так:

     def f(*args, **kwargs): print args[0] 

    и измените его на одно вот так:

     def f(a): print a 

    Причина этого не может быть выполнена – по крайней мере, без изменения фактического байт-кода Python – это потому, что они компилируются по-разному.

    Первая приводит к функции, которая получает два параметра: список и dict, а код, который вы пишете, работает в этом списке и dict. Второй результат – в функции, которая получает один параметр и к которому напрямую обращаются как локальная переменная. Если вы изменили функцию «подпись», так сказать, это приведет к такой функции:

     def f(a): print a[0] 

    который, очевидно, не сработает.

    Если вам нужна более подробная информация (хотя это вам действительно не помогает), функция, которая принимает * args или * kwargs, имеет один или два бита, установленные в f.func_code.co_flags ; вы можете изучить это сами. Функция, которая принимает регулярный параметр, имеет значение f.func_code.co_argcount установленное в 1; версия * args равна 0. Это то, что Python использует для определения того, как настроить фрейм стека функции, когда он вызывается, проверять параметры и т. д.

    Если вы хотите поиграть с изменением функции напрямую – хотя бы для того, чтобы убедить себя, что она не сработает – см. Этот ответ о том, как создать объект кода и функцию live из существующего, чтобы изменить его. (Этот материал где-то документирован, но я не могу его найти, он нигде в документах модулей типов …)

    Тем не менее, вы можете динамически изменять docstring функции. Просто назначьте func.__doc__ . Обязательно выполняйте это только во время загрузки (из глобального контекста или, скорее всего, – декоратора); если вы это сделаете позже, инструменты, которые загружают модуль для проверки docstrings, никогда его не увидят.

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

     # define a function def my_func(name, age) : print "I am %s and I am %s" % (name, age) # label the function with a backup name save_func = my_func # rewrite the function with a different signature def my_func(age, name) : # use the backup name to use the old function and keep the old behavior save_func(name, age) # you can use the new signature my_func(35, "Bob") 

    Эти результаты:

     I am Bob and I am 35 

    Редактировать 1: Отвечая на новый вопрос:

    Вы спрашиваете, как создать функцию с помощью этой подписи:

     def fun(a, b, opt=None): pass 

    Правильный способ сделать это в Python:

     def fun(a, b, opt=None): pass 

    Изменить 2: Ответ на вопрос:

    «Предположим, что у меня есть общая функция F. Я хочу программно создать функцию f2, которая ведет себя так же, как f, но имеет настраиваемую подпись».

     def f(*args, **kw): pass 

    Хорошо, тогда f2 выглядит так:

     def f2(a, b, opt=None): f(a, b, opt=opt) 

    Опять же, ответ на ваш вопрос настолько тривиален, что вы, очевидно, хотите узнать что-то другое, что вы просите. Вам действительно нужно прекратить задавать абстрактные вопросы и объяснить вашу конкретную проблему.

    Python - лучший язык программирования в мире.