Home > OS >  Improve python code with many if statements
Improve python code with many if statements

Time:09-17

I've seen a code on a project and would like to know how to write a code with less statements.

Here is the code:

    if not self.test_done:
        phase = "Test"
    else:
        if self.need_video and not self.video_sent:
            phase = 'Video'
        else:
            if not self.screening1_made and not self.screening2_made:
                phase = 'Screening 1'
            else:
                if ( self.screening1_made and not self.screening2_made ) or not self.passed_screening:
                    phase = 'Screening 2'
                else:
                    if not self.passed_phase1:
                        phase = 'Interview 1'
                    else:
                        if not self.passed_phase2:
                            phase = 'Interview 2'
                        else:
                            if not self.passed_phase3:
                                phase = 'Interview 3'
                            else:
                                if not self.got_offer:
                                    phase = 'Waiting offer decision'
                                else:
                                    if self.accepted_offer is None:
                                        phase = 'Waiting decision from candidate'
                                    elif self.accepted_offer == "Accepted":
                                        phase = 'Accepted offer'
                                    elif self.accepted_offer == "Declined":
                                        phase = 'Declined offer'

What would be best practices to avoid nested if statements?

CodePudding user response:

You can extract it to the method and early return, also you can use a dictionary lookup to get your output.

OFFER = {
    None: "Waiting decision from candidate",
    "Accepted": "Accepted offer",
    "Declined": "Declined offer",
}


def some_function_name(self):
    if not self.test_done:
        return "Test"

    if self.need_video and not self.video_sent:
        return "Video"

    if not self.screening1_made and not self.screening2_made:
        return "Screening 1"

    if (self.screening1_made and not self.screening2_made) or not self.passed_screening:
        return "Screening 2"
    if not self.passed_phase1:
        return "Interview 1"
    if not self.passed_phase2:
        return "Interview 2"
    if not self.passed_phase3:
        return "Interview 3"
    if not self.got_offer:
        return "Waiting offer decision"

    return OFFER.get(self.accepted_offer, None)

CodePudding user response:

This smells to me like a state machine. A "switch - case" construct is often used when evaluating state machines, and while python doesn't currently have that exact feature yet (3.10 will introduce match: case), A list of elif's should do the trick just fine:

if not self.test_done:
    phase = "Test"
elif self.need_video and not self.video_sent:
    phase = 'Video'
elif self.need_video and not self.video_sent:
    phase = 'Video'
elif not self.screening1_made and not self.screening2_made:
    phase = 'Screening 1'
elif ( self.screening1_made and not self.screening2_made ) or not self.passed_screening:
    phase = 'Screening 2'
elif not self.passed_phase1:
    phase = 'Interview 1'
elif not self.passed_phase2:
    phase = 'Interview 2'
elif not self.passed_phase3:
    phase = 'Interview 3'
elif not self.got_offer:
    phase = 'Waiting offer decision'
elif self.accepted_offer is None:
    phase = 'Waiting decision from candidate'
elif self.accepted_offer == "Accepted":
    phase = 'Accepted offer'
elif self.accepted_offer == "Declined":
    phase = 'Declined offer

flattened it's not so bad to look at, and you probably won't find a significantly more elegant solution without changing how you track all the conditionals.

CodePudding user response:

You can use lambda to run expressions. In the example below, we itterate through the list of tuples, containing lambda expressions and strings. When we find a lambda expression that is true, we break out of the loop.

x = 1

expressions = [
    (lambda: x == 0, 'X is equal to 0'),
    (lambda: x == 1, 'X is equal to 1'),
    (lambda: x == 2, 'X is equal to 2'),
]

phrase = 'No result found'

for item in expressions:
    expression = item[0]
    text = item[1]

    if expression():
        phrase = text
        break


print(phrase)

This is the result.

X is equal to 1

CodePudding user response:

You should refactor your program. Here's a rough Interview class and related objects, which includes a basic dictionary for event names and number of rounds.

class InterviewEvent:
    def __init__(self, event_id, event_name, total_rounds) -> None:
        self.event_id: int = event_id
        self.event_name: str = event_name
        self.total_rounds: int = total_rounds
        self.competed_rounds: int = 0
        self.completed: bool = False

class Interview:
    """Interview class to track interview progress.
    """
    def __init__(self, expected_workflow: dict = None):
        self.events = []
        self.current_event: InterviewEvent = None
        if expected_workflow:
            self.__init_events(expected_workflow)

    def __init_events(self, wf: dict) -> None:
        """Populate events list from workflow dictionary."""
        self.events = [InterviewEvent(i, k, v) for i, (k, v) in enumerate(wf.items())]
        # Set current event
        self.current_event = self.events[0]

    def completed_event_round(self, title, round_number):
        """Update round number for event.  If complete, increment
        to next round or next event in workflow.
        """
        for event in self.events:
            if event.event_name.lower() == title.lower():
                if event.competed_rounds < round_number:
                    event.competed_rounds = round_number
                
                # Mark event completed if completed rounds equals total rounds.
                if event.competed_rounds == event.total_rounds:
                    event.completed = True

                if event.completed:
                    # Increment current event by 1.
                    self.current_event = self.events[event.event_id   1]
    
    def __event_attrs(self, e: InterviewEvent) -> str:
        return "\n".join([f"{k}: {v}" for k, v in e.__dict__.items()])

    @property
    def view_current(self):
        print(self.__event_attrs(self.current_event))

    @property
    def remaining_events(self):
        return [e for e in self.events if not e.completed]

    @property
    def view_remaining_events(self):
        events_remaining = self.remaining_events
        if events_remaining:
            print("\n\n".join([self.__event_attrs(e) for e in events_remaining]))

    @property
    def event_ids(self):
        """Return all current event IDs."""
        return [e.event_id for e in self.events]

    @property
    def event_names(self):
        """Return all current event names."""
        return [e.event_name for e in self.events]


# Dictionary of event names and number of rounds.
workflow_rounds = dict(
    Test = 1,
    Video = 1,
    Screening = 2,
    Interview = 3,
)

View events that are not complete.

interview.view_remaining_events

Output:

event_id: 0
event_name: Test
total_rounds: 1
competed_rounds: 0
completed: False

event_id: 1
event_name: Video
total_rounds: 1
competed_rounds: 0
completed: False

event_id: 2
event_name: Screening
total_rounds: 2
competed_rounds: 0
completed: False

event_id: 3
event_name: Interview
total_rounds: 3
competed_rounds: 0
completed: False

View current event

interview.view_current

Output:

    event_id: 0
    event_name: Test
    total_rounds: 1
    competed_rounds: 0
    completed: False

Mark first Test round as complete

interview.completed_event_round(title = "Test", round_number = 1)

Again, view events that are not complete.

interview.view_remaining_events

Output:

event_id: 1
event_name: Video
total_rounds: 1
competed_rounds: 0
completed: False

event_id: 2
event_name: Screening
total_rounds: 2
competed_rounds: 0
completed: False

event_id: 3
event_name: Interview
total_rounds: 3
competed_rounds: 0
completed: False

Finally, view current event

interview.view_current

Output:

    event_id: 1
    event_name: Video
    total_rounds: 1
    competed_rounds: 0
    completed: False
  • Related