I have a solution using a function object type from my personal library, the below class 'algorithm'
# algorithm/__init__.py
__version__ = '2019.06.18'
from functools import partial, singledispatch
import types
#========================================
# Obtention de la documentation
#========================================
def doctopic_assembly():
"""Returns a assembly of documentation topics for this module"""
from .doc.main import doctopic_assembly
return doctopic_assembly()
_property = property
@singledispatch
def property(func):
return _property(func)
class algorithm_meta(type):
def __new__(meta, class_name, bases, new_attrs):
cls = type.__new__(meta, class_name, bases, new_attrs)
return cls
def __call__(cls, *args, **kwargs):
instance = cls.__new__(cls)
instance.kwargs = kwargs
instance.__dict__.update(kwargs)
return instance.run(*args)
#try:
#return instance.run(*args)
#except TypeError:
#print(instance, args)
#raise
def __get__(cls, obj, objtype=None):
# This allows to use the algoritm type as a method
if obj is None:
return cls
return partial(_algorithm_as_method, cls, obj)
property = staticmethod(property)
def _algorithm_as_method(cls, obj, *args, **kwargs):
instance = cls.__new__(cls)
instance.o = obj
instance.kwargs = kwargs
instance.__dict__.update(kwargs)
return instance.run(*args)
@property.register(algorithm_meta)
def _(cls):
return _property(partial(_algorithm_as_method, cls))
class algorithm(metaclass=algorithm_meta):
def run(self, *args):
return self
To implement the desired behaviour, I now define a subclass 'returnable' of 'algorithm', then I'm ready to implement your tasks B() and A(). Look at the code starting from 'class B(returnable)'
# paillasse/pf/deepreturn.py - example code
from algorithm import algorithm
class Return(Exception):
pass
class returnable(algorithm):
def run(self, *args):
try:
return self.main(*args)
except Return as exc:
if exc.args[0] is self:
return exc.args[1]
else:
raise
def return_(self, value=None):
raise Return(self, value)
class B(returnable):
def main(self, x):
self.subtask0(x)
self.subtask1(x)
def subtask0(self, x):
if x == 0:
self.return_('Hello!')
def subtask1(self, x):
if x == 1:
self.return_('World!')
def A():
print(B(0))
print(B(1))
print(B(2))
if __name__ == '__main__':
A()
My output
Output:
λ python paillasse/pf/deepreturn.py
Hello!
World!
None
By the way, it seems that the ability to return from any subtask is a nice improvement of the algorithm class. I'm considering incorporating this feature in the library. The main drawback I see is that the code is less explicit because something that looks like a function call may actually be a return statement.