Search code examples
pythonoopinheritancerefactoringiterable-unpacking

How to return different count of multiple values from Python function


I have legacy class hierarchy which I want to extend with new class with different method signature: overrided method should return tree values instead of two. Third return value for legacy classes should be some default value. Example below (repl.it).

class BaseClass:
    def do_work(self):
        param1, param2, param3 = self.get_params()
        self._process(param1, param2, param3)

    def get_params(self):
        # this method shoud be overrided in the child class
        raise NotImplementedError()

    def _process(self, param1, param2, param3):
        # do some process, for example, print params
        print(param1, param2, param3)


class LegacyChild(BaseClass):
    def get_params(self):
        return 1, 2, 3


class NewChild(BaseClass):
    def get_params(self):
        return 1, 2


legacy_child = LegacyChild()
legacy_child.do_work()

new_child = NewChild()
new_child.do_work()

In this case I get ValueError: not enough values to unpack (expected 3, got 2) on third line. And this is correct.

My first thought to solve this was to change hardcoded three params to tuple with variable length and then set param1, param2 and param3 based on it's length:

class BaseClass:
    def do_work(self):
        params = self.get_params()

        if len(params) == 3:
            param1, param2, param3 = params
        else:
            param1, param2 = params
            param3 = 3  # some default value

        self._process(param1, param2, param3)

    def get_params(self):
        # this method shoud be overrided in the child class
        raise NotImplementedError()

    def _process(self, param1, param2, param3):
        # do some process, for example, print params
        print(param1, param2, param3)

I think that this solution is too rough (repl.it).

Is there any other possible solution for that problem?


Solution

  • Maybe this?

    param1, param2, *param3 = self.get_params() # param3 is an array. When there are only 2 elements, it's empty.
    self._process(param1, param2, (param3 or [3])[0]) # 3 is the default value
    

    This is assuming that get_params will always produce 2 or 3 elements. It can't be easily extended to handle methods that return only one element.