Простой и эффективный способ прокси и обертывания вызовов методов - PullRequest
1 голос
/ 13 июля 2020

Я пытаюсь обернуть определенные c вызовы методов класса методом его подкласса. Я мог бы сделать это один за другим, например:

class Subclass(ParentClass):
    def _handle_response(self, data, _suppress=None):
        # process data
        return data

    def foo(self, *a, _suppress=None, **kw):
        return self._handle_response(super().foo(*a, **kw), _suppress=_suppress)

    def bar(self, *a, _suppress=None, **kw):
        return self._handle_response(super().bar(*a, **kw), _suppress=_suppress)

Но поскольку существует ~ 20 методов, которые нужно обернуть таким образом, это кажется мне совершенно избыточным. Другой способ, который я придумал, - использовать __getattribute__ следующим образом:

class Subclass(ParentClass):
    def _handle_response(self, data, _suppress=None):
        # process data
        return data

    def __getattribute__(self, attr):
        if attr in {"foo", "bar"}:
            def wrapped(_self, *a, _suppress=None, **kw):
                return self._handle_response(
                    getattr(ParentClass, attr)(_self, *a, **kw),
                    _suppress=_suppress
                )
            return wrapped
        return super().__getattribute__(attr)

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

Кто-нибудь знает, как к этому подойти?

Ответы [ 2 ]

1 голос
/ 13 июля 2020

Классы создаются во время выполнения, поэтому вы все равно можете изменять их после объявления и добавлять нужные методы.

class ParentClass():
    def foo(self, *a, _suppress=None, **kw):
        return "foo"

    def bar(self, *a, _suppress=None, **kw):
        return "bar"

class Subclass(ParentClass):
    def _handle_response(self, data, _suppress=None):
        return data + " plus"

methods_to_wrap = ["foo", "bar"]

for method in methods_to_wrap:
    def make_wrapper(m):
        def wrap(self, *a, _suppress=None, **kw):
            return self._handle_response(getattr(super(type(self), self), m)(*a, **kw), _suppress=_suppress)
        return wrap
    setattr(Subclass, method, make_wrapper(m=method))

o = Subclass()
print(o.foo())
print(o.bar())

печатает

foo plus
bar plus

Причина, по которой я использую * Функция 1007 * позволяет избежать позднего связывания . Остальное не требует пояснений.

0 голосов
/ 13 июля 2020

Для всех, кому интересно, вот как я решил проблему на основе ответа zxzak .

class Subclass(ParentClass):
    def __new__(cls, *args, **kwargs):
        for method in ("foo", "bar", ...):
            def make_wrapper(m):
                def wrap(self, *a, _suppress=None, **kw):
                    return self._handle_response(getattr(super(type(self), self), m)(*a, **kw), _suppress=_suppress)
                return wrap
            setattr(cls, method, make_wrapper(m=method))
        return super().__new__(cls, *args, **kwargs)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...