1
\$\begingroup\$

I have the following scenario where most of my functions require to return different things based on a condition.

def get_root_path(is_cond_met=False):
    if is_cond_met:
        return "something"
    else
        return "something else"

def get_filename(is_cond_met=False):
    if is_cond_met:
        return "file name A"
    else
        return "file name B"

is_cond_met is going to be common for all the functions I am calling. I have just put two here however I have more than 15.

The above code works, however, doesn't seem optimal, or pythonic. Is there a better solution for this?

\$\endgroup\$
4
  • \$\begingroup\$ Are all those functions depend on the same "global" boolean variable? \$\endgroup\$ – RomanPerekhrest Mar 6 '20 at 15:50
  • \$\begingroup\$ @RomanPerekhrest global are error-prone in python and so I don't want to use them. \$\endgroup\$ – Gaurang Shah Mar 6 '20 at 15:51
  • \$\begingroup\$ Then why did you write "is_cond_met is going to be common for all the functions I am calling" ? \$\endgroup\$ – RomanPerekhrest Mar 6 '20 at 15:53
  • \$\begingroup\$ Globals are error prone because they make it hard to track the data flow in and out of functions. If you simply use them as a "read-only" flag, they can be perfectly fine. \$\endgroup\$ – AlexV Mar 6 '20 at 16:07
2
\$\begingroup\$

Your example is a little too example-y for me to know if this is actually right for whatever you're actually doing, but if you have two parallel implementations of a set of behavior within the same set of functions, you might think about moving them into classes:

from abc import ABC, abstractmethod


class CondFile(ABC):

    @abstractmethod
    def get_root_path(self) -> str:
        pass

    @abstractmethod
    def get_filename(self) -> str:
        pass


class CondTrueFile(CondFile):

    def get_root_path(self) -> str:
        return "something"

    def get_filename(self) -> str:
        return "file name A"


class CondFalseFile(CondFile):

    def get_root_path(self) -> str:
        return "something else"

    def get_filename(self) -> str:
        return "file name B"


if is_cond_met:
    cond_file = CondTrueFile()
else:
    cond_file = CondFalseFile()
print(cond_file.get_root_path())
print(cond_file.get_filename())

It really depends what the condition represents, but if it results in completely different behavior across fifteen functions, it's a good clue that it represents a different "kind of" thing that your program is dealing with, i.e. a type, which is in turn a good clue that you want a different class to represent that type in your code.

\$\endgroup\$
1
\$\begingroup\$

If the functions are simply returning constants as in your example you could make a dictionary as a lookup table and do something like this:

ANSWERS = {
    'root_path' : { True:  'something'     , False: 'something else' },
    'file_name' : { True:  'file name A'   , False: 'file name B'    },
}

def get_answer(question, is_cond_met=False):
    return ANSWERS[question][is_cond_met]

# usage example
print(get_answer('file_name', True))

Perhaps you want to add some more specific error checking:

def get_answer(question, is_cond_met=False):
    try:
        answers = ANSWERS[question]
    except KeyError:
        msg = "Unknown question '{}' for get_answer".format(question)
        raise ValueError(msg)
    return answers[is_cond_met]
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.