How to define three methods circularly?

I have an abstract class with three methods, which are the semantic equivalent - they can all be defined in terms of each other, using some expensive conversion functions. I want to be able to write a derived class that would only have 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 that I can explicitly tell each derived class which transforms to use. I want to know if there is a way for the parent class to figure it out on their own without changing the children.

+7
python inheritance
source share
4 answers

You can resort to metaprogramming methods, such as writing a metaclass that automatically fills the rest of the methods, or use introspection to look at classes in type(self).mro() to find out which methods have been overridden. However, these options firmly fall into the category of “too much magic” for me, so I would go with something simpler.

Just divide each method into two: one general and one actual. 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 

General logic can also be taken into account in the 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)) 
+3
source share

I suggest defining transformations in a clean circle, so redefining one method will break that circle. I.e:

 class FooBarBaz(object): def foo(self, x): return foo_from_baz(self.baz(x)) def bar(self, x): return bar_from_foo(self.foo(x)) def baz(self, x): return baz_from_bar(self.bar(x)) 

Edited simultaneously with a comment on the same topic: This should work, but, of course, sometimes you will need to convert twice. To avoid this, you can implement some lazy evaluation, that is, functions first generate an object that knows how to evaluate itself, but first, when you request its value, the operation is performed. In this way, the conversion chain can be simplified before evaluation if it is expensive.

+2
source share

The decorator can also do the trick. Something like that:

 def define_missing(cls): has_at_least_one = False if hasattr(cls, 'foo'): if not hasattr(cls, 'bar'): cls.bar = lambda self, x: bar_from_foo(self.foo(x)) if not hasattr(cls, 'baz'): cls.baz = lambda self, x: baz_from_foo(self.foo(x)) has_at_least_one = True if hasattr(cls, 'bar'): if not hasattr(cls, 'foo'): cls.foo = lambda self, x: foo_from_bar(self.bar(x)) if not hasattr(cls, 'baz'): cls.baz = lambda self, x: baz_from_bar(self.bar(x)) has_at_least_one = True if hasattr(cls, 'baz'): if not hasattr(cls, 'bar'): cls.foo = lambda self, x: foo_from_baz(self.baz(x)) if not hasattr(cls, 'baz'): cls.bar = lambda self, x: bar_from_baz(self.baz(x)) has_at_least_one = True if not has_at_least_one: raise TypeError("Class needs to implement at least one of the methods foo, bar, and baz.") return cls 

Then use it like this:

 @define_missing class Derived1(FooBarBaz): def bar(self, x): return 5 

This is inspired by functools.total_ordering decorator.

+2
source share

I actually duplicate Dr. V answer . I am posting my answer to justify the effort I spent on creating the code:

 #!/usr/bin/env python def foo_from_bar(x): return 'foo_from_bar(%s)' % x def bar_from_baz(x): return 'bar_from_baz(%s)' % x def baz_from_foo(x): return 'baz_from_foo(%s)' % x class FooBarBaz(object): def foo(self, x): return foo_from_bar(self.bar(x)) def bar(self, x): return bar_from_baz(self.baz(x)) def baz(self, x): return baz_from_foo(self.foo(x)) class Derived1(FooBarBaz): def bar(self, x): return 5 class Derived2(FooBarBaz): def foo(self, x): return 6 class Derived3(FooBarBaz): def baz(self, x): return 7 d1 = Derived1() d2 = Derived2() d3 = Derived3() def check(expr): print expr, '->', eval(expr) for i,d in enumerate([d1, d2, d3]): print '--- d = Derived%d() ----' % (i+1) check('d.foo(0)') check('d.bar(0)') check('d.baz(0)') 

Output:

 --- d = Derived1() ---- d.foo(0) -> foo_from_bar(5) d.bar(0) -> 5 d.baz(0) -> baz_from_foo(foo_from_bar(5)) --- d = Derived2() ---- d.foo(0) -> 6 d.bar(0) -> bar_from_baz(baz_from_foo(6)) d.baz(0) -> baz_from_foo(6) --- d = Derived3() ---- d.foo(0) -> foo_from_bar(bar_from_baz(7)) d.bar(0) -> bar_from_baz(7) d.baz(0) -> 7 
+1
source share

All Articles