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.
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.
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.
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.
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With