Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define three methods circularly?

I have an abstract class with three methods that are is a sense equivalent - they could all be defined in terms of each other using some expensive conversion functions. I want to be able to write a derived class which would only need to override one of the methods and automatically get the other two. Example

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

I know I could explicitly tell each derived class which conversions to use. I want to know if there is a way for the parent class to figure this out on its own, without modifying the children.

like image 868
Karolis Juodelė Avatar asked Nov 04 '16 09:11

Karolis Juodelė


People also ask

What are the 3 principles of circular economy?

A circular economy. It is based on three principles, driven by design: eliminate waste and pollution, circulate products and materials (at their highest value), and regenerate nature.

What are circular design methods?

Specifically, this form of design advocates rethinking the process of creating a product from the beginning and, to do so, designers must adopt sustainability and respect for the environment as a starting point. The ultimate aim of circular design is to protect the environment.

What is a circular definition in philosophy?

Circular reasoning (Latin: circulus in probando, "circle in proving"; also known as circular logic) is a logical fallacy in which the reasoner begins with what they are trying to end with.


1 Answers

You could resort to metaprogramming techniques like writing a metaclass that fills in the remaining methods automatically, or use introspection to look at the classes in type(self).mro() in turn to find out which methods have been overridden. However, these options firmly fall into the "too much magic" category for me, so I'd go with something simpler.

Simply split each method up into two: One generic one, and the actual implementation. Derived classes override the actual implementation:

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

The common logic can also be factored out in a decorator:

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))
like image 61
Sven Marnach Avatar answered Sep 19 '22 05:09

Sven Marnach