0

This is more of a why, rather than a "how do I fix this" question.

I'm attempting to take an input of time, whether seconds, minutes, hours, or days, and then return the number of seconds that measurement is equal to with this code:

#!/usr/bin/env python3

valid_measurements = {
    "s": "1",
    "m": "60",
    "h": "3600",
    "d": "86400"
}


def print_err(err_type):
    if err_type == "time_format":
        print('\tTime should be entered as s/m/h/d')
        print('\t\ts = seconds')
        print('\t\tm = minutes')
        print('\t\th = hours')
        print('\t\td = days')
        print('\tFormat: "30s" or "20m" or "1h"')
        print('\tFormat: "30 s" or "20 m" or "1 h" ')


def input_time(time_type):
    time_value = 0
    multiplier = 0

    time = input("Enter " + time_type + ": ")

    if time[-1] in valid_measurements:
        measurement = time[-1]
        time_value = int(time[0:-1].rstrip())
        multiplier = int(valid_measurements[measurement])
    else:
        print_err("time_format")
        # For some reason this returns 0 for either value
        input_time(time_type)

    return time_value * multiplier


def main():
    work_time = input_time("Work Time")
    break_time = input_time("Break Time")

    print("Real work time: " + str(work_time))
    print("Real break time: " + str(break_time))

main()

However, I get this output when I attempt to break the code:

Enter Work Time: 20
    Time should be entered as s/m/h/d
        s = seconds
        m = minutes
        h = hours
        d = days
    Format: "30s" or "20m" or "1h"
    Format: "30 s" or "20 m" or "1 h" 
Enter Work Time: 20m
Enter Break Time: 5m
Real work time: 0
Real break time: 300

Process finished with exit code 0

Why does real work time return 0, despite me using the correct format for the input on the second round of the function when it calls itself? Additionally, is there anything I can do to clean up this code and make it more efficient or python-esque?

1 Answer 1

2

The Problem

else:
    print_err("time_format")
    # For some reason this returns 0 for either value
    input_time(time_type)

You recursively call input_time, but never store its output. What ends up happening is it will keep recursively calling the function, and once you have a valid input, the return will never be stored and when you reach the final call in the recursive stack, return time_value * multiplier will be 0 because time value and multiplier are still 0.

Simple Fix

One way to simply fix this is to return the output of the recursive call:

else:
    print_err("time_format")
    return input_time(time_type)

A Better Fix

It's not a good idea to use recursion for invalid user input since you can theoretically have a stack overflow error. Instead use a while loop:

def input_time(time_type):
    time_value = 0
    multiplier = 0

    time = input("Enter " + time_type + ": ")

    while time[-1] not in valid_measurements:
        print_err("time_format")
        time = input("Enter " + time_type + ": ")

    measurement = time[-1]
    time_value = int(time[0:-1].rstrip())
    multiplier = int(valid_measurements[measurement])

    return time_value * multiplier

Making it Pythonic

Finally, one thing you can change to make you're code more "pythonic" is to replace your last line of code (the main() call) with this:

if __name__ == "__main__":
    main()

When you import a python module it will run the entire file, so for example if you had another file that wanted to use the functions you created in this file, when you import this file, it would've inadvertently run main(). To prevent this, you would use the above, which only runs main() if the file was called directly (aka name == "main").

Sign up to request clarification or add additional context in comments.

2 Comments

I kind of get this, but I feel like I'm understanding this problem like a loop; for example in psuedocode while i<1; do i++; i=0; done
Your first response prompted my initial comment, however, your edit (specifically the better fix) works exactly as it should and is much cleaner than my original code. I think I'm starting to understand your original explanation in that I don't store a value for the nth run of my function, but in my head the original function should return the value of the iterative runs because it runs inside the function. I should have stored the nth version of the function as the variable I plan on returning to get it to act as I wanted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.