如何循环定义三种方法?

Kar*_*elė 7 python inheritance

我有一个抽象类,有三个方法是等同的意义 - 它们都可以使用一些昂贵的转换函数相互定义.我希望能够编写一个派生类,只需要覆盖其中一个方法并自动获取另外两个方法.例

class FooBarBaz(object):
    def foo(self, x):
        return foo_from_bar(self.bar(x))
        # OR return foo_from_baz(self.baz(x))

    def bar(self, x):
        return bar_from_foo(self.foo(x))
        # OR return bar_from_baz(self.baz(x))

    def baz(self, x):
        return baz_from_bar(self.bar(x))
        # OR return baz_from_foo(self.foo(x))

class Derived1(FooBarBaz):
    def bar(self, x):
        return 5
        # at this point foo = foo_from_bar(5) and
        # baz = baz_from_bar(5), which is what I wanted

class Derived2(FooBarBaz):
    def foo(self, x):
        return 6
        # at this point bar = bar_from_foo(6) and
        # baz = baz_from_bar(bar_from_foo(6)),
        # which is not ideal, but still works

class Derived3(FooBarBaz):
    def baz(self, x):
        return 7
        # at this point foo and bar remain defined
        # in terms of each other, which is a PROBLEM
Run Code Online (Sandbox Code Playgroud)

我知道我可以明确地告诉每个派生类要使用哪些转换.我想知道父类是否有办法自行解决这个问题,而不修改子项.

Sve*_*ach 3

您可以诉诸元编程技术,例如编写一个自动填充剩余方法的元类,或者使用内省依次查看这些类type(self).mro()以找出哪些方法已被覆盖。然而,这些选项对我来说完全属于“太多魔法”类别,所以我会选择更简单的东西。

只需将每个方法分为两部分:一个是通用方法,另一个是实际实现。派生类覆盖实际实现:

class FooBarBaz(object):

    def foo_impl(self, x):
        raise NotImplementedError

    def foo(self, x):
        try:
            return self.foo_impl(x)
        except NotImplementedError:
            try:
                return foo_from_bar(self.bar_impl(x))
            except NotImplementedError:
                return foo_from_baz(self.baz_impl(x))

    # Similarly fo bar and baz

class Dervied(FooBarBaz):

    def bar_impl(self, x):
        return 5
Run Code Online (Sandbox Code Playgroud)

通用逻辑也可以在装饰器中分解:

def first_implemented(func):
    @functools.wraps
    def wrapper(*args, **kwargs):
        for f in func(*args, **kwargs):
            try:
                return f()
            except NotImplementedError:
                pass
        raise NotImplementedError
    return wrapper

class FooBarBaz(object):

    def foo_impl(self, x):
        raise NotImplementedError

    @first_implemented
    def foo(self, x):
        yield lambda: self.foo_impl(x)
        yield lambda: foo_from_bar(self.bar_impl(x))
        yield lambda: foo_from_baz(self.baz_impl(x))
Run Code Online (Sandbox Code Playgroud)