Python中最简单的转发类是什么?

5
我希望能在Python中实现一个转发类,该类实现了某个基类,并在其构造函数中接受另一个相同基类的实现,对于基类中的每个方法都将转发到该类。
这样做可以提供一个自定义点,为某些方法添加缓存、记录每个调用点周围的日志等功能。
手动编写这个类很简单。给定这个基类:
class BaseClass(object):
    def method_a(self, *args):
        pass

    @property
    def prop_b(self):
        pass

    def method_c(self, a, b, **kwargs):
        pass

这是一个简单的转发实现。
class ForwardBaseClass(BaseClass):
    def __init__(self, underlying_implementation):
        self._impl = underlying_implementation

    def method_a(self, *args):
        return self._impl.method_a(*args)

    @property
    def prop_b(self):
        return self._impl.prop_b

    def method_c(self, a, b, **kwargs):
        return self._impl.method_c(a, b, **kwargs)

仅编写代码的缺点是,有很多样板代码,并且必须为每个方法编写该代码。随着方法添加到基类中,类似的代码必须添加到转发类中;如果希望更改类在其转发方面的处理方式,则必须将其传播到所有方法中。 (例如,我们想更改类以使其接受上下文管理器,并在每个调用周围使用它。)
是否有一种简短的Pythonic方法可以在基本语言、内置模块或PyPI上的某些可靠模块中实现此目的?
期望功能:
确切的方法签名
转发的签名应与基类匹配 每个签名上没有*args,**kwargs
使用@property装饰器的方法被正确包装
____方法(如果存在)被正确包装
与直接实现相比,每个方法调用没有额外的成本
dir(instance)执行与直接实现相同的操作
好要有:
当基类不满足某些必要条件时出错信息
有意义的文档字符串
能够从生成的对象派生
使用@staticmethod装饰器的方法被转发到传入实现的类
使用@classmethod装饰器的方法被转发到传入实现的类
__init__和__new__不会被转发
可能存在额外复杂性的地方:
基类具有元类
基类有自己的多个基类
一些更奥妙的____及其语义
不真正关心:
多个基类
除@property、@staticmethod和@classmethod之外的自定义描述符装饰器
在转发类创建后修改基类

你可以使用 getattr 将除了 __init____new__(和其他不想要的东西)之外的所有内容转发到 self._impl。尽管如此,这可能不具备你所需的全部属性... - hiro protagonist
坦白地说,我怀疑是否有一种简短、Pythonic或其他方式来做到这一点! - jonrsharpe
特别是"没有额外的方法调用费用"似乎很难满足,至少如果您想捕捉各种奇怪的情况。例如您建议的解决方案将调用重定向到self._impl对象 - 这个额外的调用每次都会增加一些额外的成本。此外,使dir(instance)能够工作的要求意味着需要一些更多的工作,这可能会影响每次调用的成本。 - skyking
1个回答

1

我只是想澄清一下我的评论:我的意思是大致如此(虽然还需要更多的工作……而且再次强调:可能不能满足您的所有要求):

class BaseClass(object):
    def method_a(self, *args):
        pass

    @property
    def prop_b(self):
        pass

    def method_c(self, a, b, **kwargs):
        pass

class ForwardBaseClass(BaseClass):
    def __init__(self, underlying_implementation):
        self._impl = underlying_implementation

    def __getattr__(self, name):
        # here you may have to exclude thigs; i.e. forward them to
        # self.name instead for self._impl.name
        try:
            return getattr(self._impl, name)
        except AttributeError:
            # do something else...

bc = BaseClass()
fbc = ForwardBaseClass(bc)
print(fbc.prop_b)
print(fbc.method_c(1,2,c=5))
print(dir(fbc))

请注意,由于Python名称修饰的影响,您将无法通过此方式访问以__开头的成员。


1
这对大多数__special__方法不起作用,因为它们是通过插槽查找的,而不是通过__getattr__查找的。您可能还想实现__dir__以列出包装对象的属性。 - Blckknght
我同意(并且已经提到了你的一个观点)。但只要没有OP的反应,我就不会进一步考虑这个想法... - hiro protagonist
1
我觉得我之前的评论比我本意中要严厉一些。你所展示的方法通常确实是最好的,提问者的标准相当愚蠢。 - Blckknght
不用担心!你的评论绝对正确、简洁明了,丝毫没有粗鲁或冒犯之意。 - hiro protagonist
@Blckknght,我总是惊讶于人们如此大胆地声称需要“相当愚蠢”,只因为他们无法想到/预见一个解决方案。让我给你举个例子——可通过编程控制的向后兼容性/回归测试。 - Anish Ramaswamy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接