10
\$\begingroup\$

I have a young friend of mine who is starting to learn Python in school and asked me to give him a little assignment. I am in no way a teacher nor a Python expert, but I accepted.

At first I thought it would be fun to start with a bit of parsing for the operation's input, like:

Enter your operation : 3+3

But it seemed a bit overwhelming for him so we agreed to separate it in three parts (first number, operand and second number).

I did a little correction but I find it clumsy and the point of the exercise is to show him some good practices.

So here is my code:

calculate = True
while calculate:
    try:
        number1 = float(input("Enter the first number : "))
    except ValueError:
            print("Incorrect value")
            exit()
    symbol = input("Enter the operation symbol (+,-,/,*,%) : ")
    try:
        number2 = float(input("Enter the second number : "))
    except ValueError:
            print("Incorrect value")
            exit()
    operande = ["+", "-", "*", "/", "%"]
    resSentence = "Result of operation \"{} {} {}\" is :".format(number1, symbol, number2)
    if symbol not in operande:
        print("Incorrect symbol")
    elif symbol == "+":
        print(resSentence, number1 + number2)
    elif symbol == "-":
        print(resSentence, number1 - number2)
    elif symbol == "*":
        print(resSentence, number1 * number2)
    elif symbol == "/":
        print(resSentence, number1 / number2)
    elif symbol == "%":
        print(resSentence, number1 % number2)
    restart = input("Do you want to do another calcul (Y/n) ? ")
    while restart.lower() != "y" and restart.lower() != "n":
        print(restart.lower(), restart.lower(), restart.lower()=="n")
        restart = input("Please, enter \"y\" to continue or \"n\" to exit the program : ")
    if restart.lower() == "n":
        calculate = False

I would have wanted to do a loop when number1 or number2 is not a valid float until the user enter a valid value, but I did not found a clean way to do it. I would gladly accept advices about this (even though I know this is not a question for this Stack Exchange, a good Pythonic way for doing this would be cool :) ).

\$\endgroup\$

2 Answers 2

9
\$\begingroup\$

Use more functions

You have a function calculate(), but if you see it does much more than just calculate, this makes your code look unreasonably clunky. But there is a very simply solution, use more functions. What if your main-loop could look like

while True:
    number1,number2,operand = take_input()
    result = calculate(number1,number2,operand)
    print(f"Answer : {numebr1} {operand} {number2} = {result}")
    if input("Do you want to play again? (y/n): ").lower() == 'n':
        break 

This makes it easier to maintain your program.

continue when there is an error

try:
    number1 = float(input("Enter the first number : "))
except ValueError:
        print("Incorrect value")
        exit()

Ask yourself, why would a program terminate if the user enters invalid input? Give him a another chance xD

Maintain consistent indentation

try:
    number2 = float(input("Enter the second number : "))
except ValueError:
            print("Incorrect value")
            exit()

Try to maintain consistent indentation, since you have used 4 spaces earlier, there is no good reason to use 8 later, it might just confuse people who read the code later.

Code logic 1

Let's consider this sample input

Enter the first number : 1
Enter the operation symbol (+,-,/,*,%) : I like python
Enter the second number : 2
Incorrect symbol

Clearly, the symbol is wrong. Why did I have to enter the second number, just to find out I made a mistake while entering the symbol? It should've told me right away that my symbol was incorrect, so I could've corrected it.

Just move the if symbol not in operands statement so it sits right next to the input.

eval

Eval in Python

This would be the biggest improvement in your program, since it converts about 10-15 lines of code, into one.

The eval() function evaluates the specified expression, if the expression is a legal Python statement, it will be executed.

That sounds familiar, aren't we basically evaluating simple expressions?

Using eval, your calculation part would look like

result = eval(f"{number1}{operand}{number2}")

Example, number1 = 5,number2 = 10, operand = '+'

This is what is basically happening

result = eval("5+10")

Final

Here is the code with the improvements

def take_input():
    err_msg = "Invalid input"
    operands = ['+','-','*','/','%']
    try:
        num1 = float(input("Enter number 1: "))
    except Exception:
        print(err_msg)
        return take_input()
    try:
        num2 = float(input("Enter number 2: "))
    except Exception:
        print(err_msg)
        return take_input()

    print("Operands: " + ', '.join(x for x in operands))
    try:
        operand = input("Enter operand: ")
    except Exception:
        print(err_msg)
        return take_input()

    if operand not in operands:
        print(err_msg)
        return take_input()

    return num1,num2,operand

def calculate(num1,num2,operand):
    return eval(f"{num1}{operand}{num2}")


def mainloop():
    while True:
        num1,num2,operand = take_input()
        result = calculate(num1,num2,operand)
        print(f"Answer: {result}")
        if input("Do you want to play again? (y/n): ").lower() == 'n':
            break

mainloop()
\$\endgroup\$
10
  • \$\begingroup\$ I really like the improvements you proposed and how you explained them to me :) As a side question do you think this would be understandable to my young friend (14 y/o) who just started python and only saw if statement in class (he is motivated though since he himself came to me to learn more about coding and so far he seems to really enjoy it). I guess the harder will be to not give him the code but to let him do some search and do trial and error by himself ^^. I will wait untill tonight (I am in France) before accepting an answer but so far, I will accept yours :) \$\endgroup\$
    – L. Faros
    Commented Oct 21, 2020 at 10:49
  • 2
    \$\begingroup\$ Well I'll do my best to explain him as clearly as possible and to let him search by himself :) Wow I would really not have guessed, you have a bright future ahead of you \$\endgroup\$
    – L. Faros
    Commented Oct 21, 2020 at 10:59
  • 1
    \$\begingroup\$ @L.Faros There are plenty of resources to learn python online. This one was extremely useful to me \$\endgroup\$
    – user228914
    Commented Oct 21, 2020 at 11:05
  • 1
    \$\begingroup\$ @L.Faros the final code could be thought of as If statements. Basically, all Try/Except is doing is saying (using the first one as an example) "If setting num1 to a float results in a Value error, print the error message and re-run take_input. If there is no error setting num1 to a float, then do that and continue to the next line after except". \$\endgroup\$
    – BruceWayne
    Commented Oct 21, 2020 at 18:34
  • \$\begingroup\$ Something I noticed, if the users second input results in an error, they have to restart and put in number 1 again. Perhaps that's by design and is intended, but one alternate would be to just ask for the second number again (via whatever method you like). Same with operand. (Though this is a simple program so it's probably overkill. Just something for OP to consider trying) \$\endgroup\$
    – BruceWayne
    Commented Oct 21, 2020 at 18:37
5
\$\begingroup\$

This is an alternative to your code, it is a bit more complex but it is also more readable. I did manage to do the loop thing but it is kind of hard to follow. Sorry.

running = True
# Break Things up into functions each function does one single thing

def calculate(inputOne, operand, inputTwo):
    """
    Calculates inputOne operand and inputTwo
    """

    if operand == "+":
        return inputOne + inputTwo
    elif operand == "-":
        return inputOne - inputTwo
    elif operand == "*":
        return inputOne * inputTwo
    elif operand == "/":
        return inputOne / inputTwo
    elif operand == "%":
        return inputOne % inputTwo

def askInput():
    """
    Asks for a number until a number is given checks if each one is valid
    """

    isValid = [False, False, False] # none of the numbers are validated yet
    number1, symbol, number2 = ["", "", ""]
    
    # Here is a good implementation of the loop, it is kind of complex though
    while True:
        try:
            if not isValid[0]: # Asks for number1 if it is not valid
                number1 = int(input("Enter the first number : "))
                isValid[0] = True

            if not isValid[1]: # This is added functionality because there was a loophole in your program
                symbol = input("Enter the operation symbol (+,-,/,*,%) : ") # use tuples whenever possible
                supportedOperands = ("+", "-", "/", "*", "%")

                if symbol not in supportedOperands:
                    raise ValueError

                isValid[1] = True

            if not isValid[2]: # Asks for number2 if it is not valid
                number2 = int(input("Enter the second number : "))
                isValid[2] = True
            break
        
        except ValueError:
            continue # this just restarts the whole thing
    
    return number1, symbol, number2



def continueApp():
    """
    Checks if the input to restart is valid
    """
    restart = input("Do You want to do another calculation (Y/n) ? ").lower()

    while True:
        if restart == "y":
            return True
        elif restart == "n":
            return False
        else:
            restart = input("Please, enter \"y\" to continue or \"n\" to exit the program : ").lower()

while running:

    numberOne, operand, numberTwo = askInput()
    answer = calculate(numberOne, operand, numberTwo)
    resSentence = f"Result of operation {numberOne} {operand} {numberTwo} is : {answer}"
    print(resSentence)

    if continueApp():
        pass
    else:
        running = False
exit()


Tips:

  • Break Things Up into functions:

Functions are just containers for code that can be executed, functions MUST do ONE and only ONE thing more on functions here.

  • Please Try to comment on your code, it makes it easier to read and edit.

This function

def calc():
    x = 1
    y = 12
    return (((x+y)/x)**y)+(3*x+4*y) # Please don't write like this in any case

would be much better with an explanation or what is going on

def calc():
    """
    Accepts: Nothing
    Does: Adds X and Y, then divides it by X to the power of Y
          then it adds  it to X multiplied by three and 4 multiplied by Y
    Returns: integer (the result of Does) ^^^^^
    """
    x = 1
    y = 12
    return ((x+y)/x**y)+(3*x+4*y) # Again, please don't write code like this
  • Use f strings (this requires python 3.6 and above)

f strings are used like this

value = "12"
print(f"Number {value} is an example of an f string") 

# Versus

print("Number {} is an example of an f string".format(value))
  • Try to space out your code

Trust me, this makes your code easier to read and understand.

def calc():
    """
    Accepts: Nothing
    Does: Adds X and Y, then divides it by X to the power of Y
          then it adds  it to X multiplied by three and 4 multiplied by Y
    Returns: integer (the result of Does) ^^^^^
    """
    x = 1
    y = 12
    ans = (x + y) / (x ** y)
    ans += (3 * x) + (4 * y) # just adds ans to the right side of the operator
    return ans
\$\endgroup\$
5
  • 1
    \$\begingroup\$ The operand could be the key of a dictionary, that leads to \$O(1)\$ search time. \$\endgroup\$ Commented Oct 21, 2020 at 0:37
  • \$\begingroup\$ Well I tried to avoid functions since he is a new learner and the exercise should be more oriented about basics programming skills (conditions, loop...) but tyvm \$\endgroup\$
    – L. Faros
    Commented Oct 21, 2020 at 0:49
  • \$\begingroup\$ Though I like your implementation, I would like to go step by step and avoid using functions since he is young and already have difficulties following a simple script \$\endgroup\$
    – L. Faros
    Commented Oct 21, 2020 at 0:54
  • \$\begingroup\$ This answer doesn't seem to be focused on code review but rather to offer an alternative solution. Would you mind explaining (edit your answer) how is this any better from the OP's code? What did you improve? \$\endgroup\$ Commented Oct 21, 2020 at 6:43
  • \$\begingroup\$ Yeah, sorry, I will fix that. \$\endgroup\$
    – tominekan
    Commented Oct 21, 2020 at 15:04

Your Answer

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

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