【问题标题】:Simple and performant way to proxy and wrap method calls代理和包装方法调用的简单而高效的方法
【发布时间】:2020-07-13 14:00:01
【问题描述】:

我想要实现的是将类的特定方法调用与其子类的方法包装起来。我可以一个一个地做,像这样:

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)

这可行,但我对每次像这样调用方法时都会创建一个新的包装函数这一事实并不满意。这是我想避免的很多不必要的开销。我可以缓存这些,但同样,这似乎不是一个非常优雅的解决方案。

有人知道如何解决这个问题吗?

【问题讨论】:

  • 这其中的开销在哪里?我假设这是网络调用的一部分。所以在任何时候,只有一个方法会被包装在一个新创建的函数中。 cmiiw.
  • 如果我对__getattribute__ 的工作方式没有完全误解,在这个例子中,每次调用foo 都会创建一个新的wrapper 函数。我通过将wrappers_created 类变量添加到Subclass 并在返回包装器之前将以下行添加到__getattribute__ 来测试它:Subclass.wrappers_created += 1。两次调用foo 后,计数器位于2
  • @leoOrion:这不适用于这里,因为在创建函数时会调用装饰器一次。您链接的问题是包装器创建了多少开销。我在说的是创建包装器的开销。 Afaik 装饰器不会在对装饰函数的每个函数调用上创建新的包装器。
  • 我添加这个是为了暗示装饰器的想法。

标签: python


【解决方案1】:

类是在运行时创建的,所以你仍然可以在声明之后修改它们并添加你想要的方法。

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

我使用make_wrapper 函数的原因是为了避免late binding。其余的应该是不言自明的。

【讨论】:

  • 非常感谢!尽管我最终并没有完全像这样实施它,但您的回答帮助我找到了令人满意的解决方案!我试图通过修改__new__ 方法来实现类似的东西,但一直遇到各种错误。我做的不同主要是查找父类的原始方法,没有解决后期绑定问题。
【解决方案2】:

对于任何感兴趣的人,这就是我最终根据zxzak's answer 解决问题的方式。

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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多