Декорирование функции для создания класса, который разделяет ключевые слова и позиционные аргументы на два других метода - PullRequest
0 голосов
/ 19 февраля 2020

У меня есть абстрактный класс, @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

1 Ответ

0 голосов
/ 19 февраля 2020

Я пытаюсь точно понять, в чем ваша проблема, поэтому давайте начнем с этого примера:

def decorator(function):

    def wrapper(*args, **kwargs):

        print(' [decorator] Printing before function [' + function.__name__ + '] execution')

        result = function(*args, **kwargs)

        print(' [decorator] Found args: ' + str(args))
        print(' [decorator] Found kwargs: ' + str(kwargs))

        print(' [decorator] Printing after function [' + function.__name__ + '] execution')

        return result

    return wrapper


@decorator
def MyFunction(*args, **kwargs):

    print('[MyFunction] Received args:' + str(args))
    print('[MyFunction] Received kwargs:' + str(kwargs))


MyFunction(1, 2, kwarg1=3, kwarg2=2)

Это приведет к:

 [decorator] Printing before function [MyFunction] execution
[MyFunction] Received args:(1, 2)
[MyFunction] Received kwargs:{'kwarg1': 3, 'kwarg2': 2}
 [decorator] Found args: (1, 2)
 [decorator] Found kwargs: {'kwarg1': 3, 'kwarg2': 2}
 [decorator] Printing after function [MyFunction] execution

Куда вам нужно обратиться проблема отсюда?

...