У меня есть абстрактный класс, @abstractmethods
, его __init__
и __call__
. В принципе, определение класса выглядит следующим образом:
class Base(abc.ABC):
@abc.abstractmethod
def __init__(self, **params):
pass
@abc.abstractmethod
def __call__(self, *input):
pass
Я хочу создать декоратор, который преобразует аргументы ключевых слов функции в аргументы в __init__
и позиционные аргументы в __call__
: так что если
def func(a, b, *args, k=1, g=2, **kwargs):
pass # it does something
тогда я бы хотел обернуть мою забаву c в декоратор, который будет выводить как в соответствии с
class Func(Base):
def __init__(self, k=1, g=2, **kwargs):
inspected_kwargs = ... # arguments of init in dictionary form
for argname, val in inspected_kwargs.items():
setattr(self, argname, val)
def __call__(self, a, b, *args):
inspected_args = ... # arguments of call in tuple form
return func(*inspected_args, **self.__dict__)
У меня было несколько попыток, используя inspect
, но я не уверен, как вставить параметры из объекта inspect.Signature
обратно для создания функции. Кроме того, у меня возникли некоторые проблемы, потому что, когда локальные методы добавляются в класс, который является внутренним по отношению к функции с простой установкой атрибута, локальные функции больше не находятся в области видимости, когда класс возвращается.
Вопрос заключается в следующем: Как создать класс внутри функции, чьи методы определены на основе сигнатуры вызываемого входа функции?
@ edit Чтобы получить более подробную информацию об идее:
def decorator(function):
class Inner(Base):
pass
# non-existent functions ahead, just to convey the idea
# the exact way to do this is exactly the matter of this question
sig = inspect.get_signature(function)
init = create_from_signature(signature=sig.keyword_arguments(),
body="for name, val in sig.keyword_arguments():\nsetattr(self, name, val)")
call = create_from_signature(signature=sig.positional(),
body="return partial(func, **self.__dict__)")
Inner.__init__ = init
Inner.__call__ = call
# it would be nice if Inner class name would also depend on
# func name:
setattr(Inner, __name__, "_".join(function.__name__, "decorated"))
return Inner
def users_function(a, b, k=5):
# user's code; does whatever, for me it's a black box
return a * b / k
Ожидаемое поведение:
decorator(users_function)(k=10)(1, 2) == users_function(1, 2, k=10)
Почему я хочу это сделать? Потому что тогда можно вызвать методы Base
для выходного объекта, для которого потребуется знание только ключевых аргументов:
my_obj = decorator(users_function)(k=10)
my_obj.basemethod() # basemethod is implementd in Base class