在 Python 对象上调用 "forward" 方法最简洁的方法是什么?

What's the most concise way to "forward" method calls on a Python object?

我正在编写一些报告生成代码,其中 class CoverageReporter 完成了所有繁重的工作,只有少数属性在 CoverageReporter 的不同实例之间发生变化。假设我们想要别名

SubsystemCoverageReporter(*args, **kwargs)

CoverageReporter('subsystem', *args, **kwargs)

以简洁的方式。目前,我是这样完成的:

class SubsystemCoverageReporter(object):
    def __init__(self, *args, **kwargs):
        self.reporter = CoverageReporter('subsystem', *args, **kwargs)

    def __getattr__(self, name):
        return getattr(self.reporter, name)

另一个子系统 2 具有几乎完全 相同的样板,例如:

class SubsystemTwoCoverageReporter(object):
    def __init__(self, *args, **kwargs):
        self.reporter = CoverageReporter('subsystem2', *args, **kwargs)

    def __getattr__(self, name):
        return getattr(self.reporter, name)

有没有更简洁+Python化的表达方式?我宁愿不依赖继承,但如果没有更好的方法,我愿意接受。

假设您的 class CoverageReporter 是这样的:

class CoverageReporter:
    def __init__(self, _type, _name):
        self.type = _type
        self.name = _name
        
    def do_something(self):
        print(f"{self.type}:{self.name} did something")
        
    def do_something_else(self):
        print(f"{self.type}:{self.name} did something else")     

您可以定义一个函数来创建 CoverageReporter 和 returns 该对象。

def SubsystemCoverageReporter(*args, **kwargs):
    return CoverageReporter('subsystem', *args, **kwargs)


def SubsystemTwoCoverageReporter(*args, **kwargs):
    return CoverageReporter('subsystem2', *args, **kwargs)

s1 = SubsystemCoverageReporter("Foo")
s2 = SubsystemTwoCoverageReporter("Bar")

s1.do_something()         # subsystem:Foo did something
s2.do_something_else()    # subsystem2:Bar did something else

请注意,这并不完全等同于您所拥有的,因为 SubsystemCoverageReporter 不是 class,所以您不会能够继承它。如果您希望该选项可用,那么我认为继承是您拥有的最佳解决方案。

class SubsystemCoverageReporter(CoverageReporter):
    def __init__(self, *args, **kwargs):
        super().__init__('subsystem', *args, **kwargs)
        
class SubsystemTwoCoverageReporter(CoverageReporter):
    def __init__(self, *args, **kwargs):
        super().__init__('subsystem2', *args, **kwargs)

s1 = SubsystemCoverageReporter("Foo")
s2 = SubsystemTwoCoverageReporter("Bar")

s1.do_something()         # subsystem:Foo did something
s2.do_something_else()    # subsystem2:Bar did something else

如果您想避免重复自己,请使用您总是会使用的相同解决方案,将重复的代码包装在一个函数中并参数化可以更改的部分:

def make_klass(subsystem):
    class SubsystemCoverageReporter(object):
        def __init__(self, *args, **kwargs):
            self.reporter = CoverageReporter(subsystem, *args, **kwargs)
    
        def __getattr__(self, name):
            return getattr(self.reporter, name)
    return SubsystemCoverageReporter


SubsystemCoverageReporter = make_klass("subsystem")
SubsystemTwoCoverageReporter = make_klass("subsystem2")

请注意,您创建的 类 将具有相同的 __name__ 属性值。那是希望不重要,但你可以绕过它,例如:

SubsystemCoverageReporter.name = "SubsystemCoverageReporter" SubsystemTwoCoverageReporter.name = "SubsystemTwoCoverageReporter"

虽然那很丑。

您可以直接使用 type 构造函数,或者如果 meta类 是一个问题,请改用 types.new_class 并且它会正确处理它。但在最简单的情况下,

def make_klass(name, subsystem):
    def __init__(self, *args, **kwargs):
        self.reporter = CoverageReporter(subsystem, *args, **kwargs)
    def __getattr__(self, name):
        return getattr(self.reporter, name)
    klass = type(name, (object,), dict(__init__=__init__, __getattr__=__getattr__))
    return klass

SubsystemCoverageReporter = make_klass("SubsystemCoverageReporter", "subsystem")
SubsystemTwoCoverageReporter = make_klass("SubsystemTwoCoverageReporter", "subsytem2")

请注意,您在这里重复了一点。

但是,通常你不应该关心 __name__ 属性