Python Forum
[split] simple calculator
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[split] simple calculator
#1
I started learning python just a week ago and has been a very interesting experience. I have been able to write my first program in python, which is a program to make a simple calculator. I want to share it here for technical comments, contributions, and ideas on how I could make it better. I also join this forum only today so pardon me if I violate any respected rules for the use of this forum. I want to share the code here:

# Program to make a simple calculator
def calculator():
    num1 = float(input("Enter first number: "))
    op = input("Operator ")
    num2 = float(input("Enter second number: "))

    if op == "+":
       print(f"Answer: {add(num1, num2)}")
    elif op == "-":
        print(f"Answer: {subtract(num1, num2)}")
    elif op == "*":
        print(f"Answer: {multiply(num1, num2)}")
    elif op == "/":
        print(f"Answer: {divide(num1, num2)}")
    elif op == "**":
        print(f"Answer: {exponent(num1, num2)}")
    elif op == "%":
        print(f"Answer: {remainder(num1, num2)}")
    else:
        print("Invalid entry!")


# This function adds two numbers:
def add(n1, n2):
    return n1 + n2


# This function subtract two numbers:
def subtract(n1, n2):
    return n1 - n2


# This function multiplies two numbers:
def multiply(n1, n2):
    return n1 * n2


# This function divides two numbers:
def divide(n1, n2):
    return n1 / n2


# This function finds the exponents of two numbers:
def exponent(n1, n2):
    return n1 ** n2


# This function finds the remainder of two numbers:
def remainder(n1, n2):
    return n1 % n2


calculator()
Gribouillis write Aug-18-2022, 02:23 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.

Also this post would better fit in a new thread in the Code sharing part of the forum. Go to this page and hit the 'Post Thread' button to start your own thread.
Reply
#2
You've made a good start.

For functions, get in to the practice of using doc strings, like this:

def add(n1, n2):
    """
Takes two floating point numbers and returns the sum
    """
That way the text can be extracted with:

print(add.__doc__)
... add, in the above example being the function name.

Personally, I wound not have the main body of the code as a function, rather have it as a body of code in its own right, with all the functions above the main body.

The next thing to do is to sanitize the user input. As an example, if a user does not enter a number, have your script catch that and once again ask for a number. This can be done without using try / except. If you want me (or anyone else) to show you how that can be done, then fair enough; just ask, but you'll learn a good deal more if you can work that out for your self. Hint: write a function to check the user input before the rest of your script accepts it.

I'll not present you with a large list of things to do right now, but there are other improvements that can be made, such as taking all of the input in one hit, then extracting the elements so that the calculation can be made.

Hint: run this...

calculate = list(input(": "))
print(calculate)
... and enter 7*4
FelixLarry likes this post
Sig:
>>> import this

The UNIX philosophy: "Do one thing, and do it well."

"The danger of computers becoming like humans is not as great as the danger of humans becoming like computers." :~ Konrad Zuse

"Everything should be made as simple as possible, but not simpler." :~ Albert Einstein
Reply
#3
Hello rob101,

You have made a great deal of comments on this code of mine and I appreciate it. I have already been knocking my head around on other ways to improve the code as you have technically advised. However, since I am totally new to Python, and this happens to be my first programming project, kindly help me input all the technical points you are raising, even though I have made some attempts to do that from my end but as an amateur, it's not been easy at all. Your help will give me a great deal of mileage in my journey.

Thank you.
rob101 likes this post
Reply
#4
(Aug-18-2022, 04:07 PM)FelixLarry Wrote: Hello rob101,

You have made a great deal of comments on this code of mine and I appreciate it. I have already been knocking my head around on other ways to improve the code as you have technically advised. However, since I am totally new to Python, and this happens to be my first programming project, kindly help me input all the technical points you are raising, even though I have made some attempts to do that from my end but as an amateur, it's not been easy at all. Your help will give me a great deal of mileage in my journey.

Thank you.

You are very welcome.

The reason that I suggested the code line calculate = list(input(": ")) is because it can achieve two objectives in one: it can be used as a part of the user input sanitization function as well as for evaluating the input as a calculation. To do these operations, you'll need to understand how a list can be 'unpacked', which means accessing the individual elements. This is a fundamental operation that you will need to learn, as it can be applied to other objects, such as a string, or a tuple, two name just two good examples.

Have a go, using whatever learning resource you're already using and come back with just some basic code that can unpack that input example, or whatever failed attempt you come up with and we'll take it from there.

Note: I'm not in control of this thread, but my guess is that it will be moved to 'Homework', but it's only my guess.
Sig:
>>> import this

The UNIX philosophy: "Do one thing, and do it well."

"The danger of computers becoming like humans is not as great as the danger of humans becoming like computers." :~ Konrad Zuse

"Everything should be made as simple as possible, but not simpler." :~ Albert Einstein
Reply
#5
Hello rob101,

Following your recommendations on my previous code for making a simple calculator, here is the little improvement I have been able to make as homework. Please check and advise.

# Program for making a simple calculator
def check_user_input(input):
    try:
        # Convert to an integer:
        i = int(input)
        print(f"Input is an integer: {i}")
    except ValueError:
        try:
            # Convert to a float:
            i = float(input)
            print(f"Input is a float: {i}")
        except ValueError:
            print(f"No, is a string. Please enter a valid number!")


val1 = input("Enter first number and hit enter: ")
check_user_input(val1)
op = input("Enter an operator and hit enter: ")
val2 = input("Enter second number and hit enter: ")
check_user_input(val2)

def calculator():
    num1 = float(val1)
    num2 = float(val2)

    if op == "+":
        print(f"Result: {add(num1, num2):,}")
    elif op == "-":
        print(f"Result: {subtract(num1, num2):,}")
    elif op == "*":
        print(f"Result: {multiply(num1, num2):,}")
    elif op == "/":
        print(f"Result: {divide(num1, num2):,}")
    elif op == "**":
        print(f"Result: {exponent(num1, num2):,}")
    elif op == "%":
        print(f"Result: {remainder(num1, num2):,}")
    else:
        print("Invalid operator entery!")


def add(n1, n2):
    '''
Takes two floating point numbers and returns the sum
    :param n1:
    :param n2:
    :return n1 + n2:
    '''
    print(add.__doc__)
    return n1 + n2


def subtract(n1, n2):
    '''
Takes two floating point numbers and returns the difference
    :param n1:
    :param n2:
    :return n1 - n2:
    '''
    print(subtract.__doc__)
    return n1 - n2


def multiply(n1, n2):
    '''
Takes two floating point numbers and returns the product
    :param n1:
    :param n2:
    :return n1 * n2:
    '''
    print(multiply.__doc__)
    return n1 * n2


def divide(n1, n2):
    '''
Takes two floating point numbers and returns the quotient
    :param n1:
    :param n2:
    :return n1 / n2:
    '''
    print(divide.__doc__)
    return n1 / n2


def exponent(n1, n2):
    '''
Takes two floating point numbers and returns the exponent
    :param n1:
    :param n2:
    :return:
    '''
    print(exponent.__doc__)
    return n1 ** n2


def remainder(n1, n2):
    '''
Takes two floating point numbers and returns the remainder
    :param n1:
    :param n2:
    :return n1 % n2:
    '''
    print(remainder.__doc__)
    return n1 % n2


calculator()
Reply
#6
I'm not sure that I would use input as an arg in your check_user_input function. Seems like you're redefining a built-in function. I would change it to something else that is not a built-in function.
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#7
Hi.

It's good to see that you are trying and learning new ways, and I understand that you want to move forward as fast as you can. To do that you must first learn the fundamentals of Python, one of which is the Data Types.

If you look back, you'll see that I asked that you simply take some input (such as calc = list(input(": ")) and then try to unpack that so that the elements can be checked for sanity. I also said that this could be done (the sanity checking) without using try / except. Maybe I confused you, and it's my bad if that is the case.

Take things one step at a time, check what you have and only move on to the next step once you have what you need. Right now what we need is the elements of the input object.

Objects such as a list or a string are indexed, starting at position zero and this basic code will demonstrate that. To keep it easy, we'll simply use the default return of the input() function, which is a string object, as I'm sure you know:

calc = input(": ")

position = 0

print(f"The length of the input is: {len(calc)}")
print("The index positions are...")
for char in calc:
    print(f"{position:} : {char}")
    position += 1
What we have there is a proof of concept; that each character of the input (char in calc) can be represented by a position number, starting at zero (position = 0) and ending at the length of said input (len(calc)).

From the above you should be able to see that we can access any of the input elements using its position in the object, such as print(calc[0]) or print(calc[1]) or print(calc[2]), ect.

We can prove that with:

for pos in range(len(calc)):
    print(f"{pos} : {calc[pos]}")
... which says, start counting at zero and stop at the end of the input (for pos in range(len(calc))) and for each loop count, output the element at that position.

If you don't know about loops and the range() function, again, this is a 'must know' concept for you to be able to move forward, so once again use whatever learning resource you have, to better understand that.

Checking the user input is possibly the most challenging part, but as with everything, it can be (and should be) broken down into its constituent parts.

So, what are we looking for? Well, for starters, we're looking for any digit. We're also looking for the operator, so let's have some lists (just as you would with pen and paper).

digits = ['0','1','2','3','4','5','6','7','8','9']
operators = ['+','-','*','/','**','%']
Note: The same advice here, as is for loops.

I know, we're also looking for the decimal point, but we'll get to that.

As I suggested (and as you attempted) a function to check the user input, could be a good plan, so...


def check_input(ui):
    """
returns a tuple object of the user input, if said input is valid,
else returns False for (q)uit or None for an invalid input
    """
    n1 = n2 = op = ''  # create three string objects to store any valid input
    digits = ['0','1','2','3','4','5','6','7','8','9']
    operators = ['+','-','*','/','**','%']
    ui = ui.lower()    # change the case for any alpha character in the input
    check = ui.strip() # remove any spaces from the input and assign it to a new variable
    if check == 'q':   # a way out for the app
        return False
    for char in check: #loop through the check and exit the function if any of these fail
        if char not in digits\
           and char not in operators\
           and char != '.': return
        # if we get to this point, we know we have a valid input
        elif char.isdigit(): # do we have a digit?
            if not op:       # have we already had the operator?
                n1 += char   # if no, then it's a part of the first number
            else:            # if yes, then it's a part of the second number
                n2 += char
        elif char == '.':    # or it could be a decimal point
            if not op:       # then it's a part of the first number after the decimal point
                n1 += char
            else:            # otherwise it's a part of the second number after the decimal point
                n2 += char
        else:                # not a digit or a decimal point, it has to be the operator
            op = char
    if op and n1 and n2:     # if we have all the elements in place, pass them back
        return op, float(n1), float(n2)
There's quite a lot going on there, so I've commented the code, but if you don't understand anything, feel free to ask.

Now that we have a valid input and we know what the numbers are, and what the operation is, we can have a function to which that information can be passed:

def calculate(n1, n2, op):
    if op == "+":
        result = n1 + n2
    elif op == "-":
        result = n1 - n2
    elif op == "*":
        result = n1 * n2
    elif op == "/":
        result = n1 / n2
    elif op == "**":
        result = n1 ** n2
    elif op == "%":
        result = n1 % n2
    return result
Let's also give a user a little help, should it be needed:

def help():
    print("\nHelp:")
    print("Simply enter a calculation as\
    \n<number><operator><number>\
    \ne.g: 1+2 or 2*7 or 7/9\n")
All we now need is a 'driver' for the above functions:

print("=======<Calculator app>=======")
 
quit = False
while not quit: # the app will now run until quit is True
    calc = input("Input Q to quit: ")
    check = check_input(calc)
    if check:
        op = check[0]
        n1 = check[1]
        n2 = check[2]
        result = calculate(n1, n2, op)
        print(f"\nResult: {result}\n")
    elif check == None:
        help()
    else:
        print("All done.")
        quit = True
I hope that you can follow this mini tutorial and that you found it helpful. If you've any questions, simply ask, and I'm sure you'll get the answers.

Note: I've checked the above code and so far as I'm aware, there are no bugs; I'll correct any that are reported.

Also, if any more experienced coder can offer me any advice on what I've done here, then I'm open to that.

Thank you.



Bug reports and fixes.

Known bug: If more than one operator is entered, the result is incorrect.
Fix: Develop this app to handle that input. This can be done in ways that include...
1. Returning a error message for such an input and abort the calculation.
2. Have the additional operation saved to some register and applied to the final calculation.
FelixLarry likes this post
Sig:
>>> import this

The UNIX philosophy: "Do one thing, and do it well."

"The danger of computers becoming like humans is not as great as the danger of humans becoming like computers." :~ Konrad Zuse

"Everything should be made as simple as possible, but not simpler." :~ Albert Einstein
Reply
#8
There is built-in operator module what can be used to map operations to specific characters:

import operator

operations = {'+': operator.add,
              '-': operator.sub,
              '*': operator.mul,
              '/': operator.truediv,
              '**': operator.pow,
              '%': operator.mod,
             }

# usage
print(operations['+'](2, 3)) 
print(operations['-'](7, 2))   
For parsing input itertools.groupby can be used. We need keyfunc that keeps together digits and '.':

from itertools import groupby

inputs = ('1+2', '1.22+1', '4 + 2')

def keyfunc(symbol):
    return symbol.isdigit() or symbol == '.'

for item in inputs:
    stream = groupby(item, key=keyfunc)
    parsed = (''.join(item[1]).strip() for item in stream)
    print(*parsed, sep='\n')
FelixLarry and rob101 like this post
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy

Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Reply
#9
(Aug-19-2022, 10:07 AM)rob101 Wrote: Also, if any more experienced coder can offer me any advice on what I've done here, then I'm open to that.

We can prove that with:
for pos in range(len(calc)):
    print(f"{pos} : {calc[pos]}")
See the forum thread [Basic] Never use "for i in range(len(sequence)):
rob101 likes this post
Reply
#10
(Aug-19-2022, 10:07 AM)rob101 Wrote: Hi.

It's good to see that you are trying and learning new ways, and I understand that you want to move forward as fast as you can. To do that you must first learn the fundamentals of Python, one of which is the Data Types.

If you look back, you'll see that I asked that you simply take some input (such as calc = list(input(": ")) and then try to unpack that so that the elements can be checked for sanity. I also said that this could be done (the sanity checking) without using try / except. Maybe I confused you, and it's my bad if that is the case.

Take things one step at a time, check what you have and only move on to the next step once you have what you need. Right now what we need is the elements of the input object.

Objects such as a list or a string are indexed, starting at position zero and this basic code will demonstrate that. To keep it easy, we'll simply use the default return of the input() function, which is a string object, as I'm sure you know:

calc = input(": ")

position = 0

print(f"The length of the input is: {len(calc)}")
print("The index positions are...")
for char in calc:
    print(f"{position:} : {char}")
    position += 1
What we have there is a proof of concept; that each character of the input (char in calc) can be represented by a position number, starting at zero (position = 0) and ending at the length of said input (len(calc)).

From the above you should be able to see that we can access any of the input elements using its position in the object, such as print(calc[0]) or print(calc[1]) or print(calc[2]), ect.

We can prove that with:

for pos in range(len(calc)):
    print(f"{pos} : {calc[pos]}")
... which says, start counting at zero and stop at the end of the input (for pos in range(len(calc))) and for each loop count, output the element at that position.

If you don't know about loops and the range() function, again, this is a 'must know' concept for you to be able to move forward, so once again use whatever learning resource you have, to better understand that.

Checking the user input is possibly the most challenging part, but as with everything, it can be (and should be) broken down into its constituent parts.

So, what are we looking for? Well, for starters, we're looking for any digit. We're also looking for the operator, so let's have some lists (just as you would with pen and paper).

digits = ['0','1','2','3','4','5','6','7','8','9']
operators = ['+','-','*','/','**','%']
Note: The same advice here, as is for loops.

I know, we're also looking for the decimal point, but we'll get to that.

As I suggested (and as you attempted) a function to check the user input, could be a good plan, so...


def check_input(ui):
    """
returns a tuple object of the user input, if said input is valid,
else returns False for (q)uit or None for an invalid input
    """
    n1 = n2 = op = ''  # create three string objects to store any valid input
    digits = ['0','1','2','3','4','5','6','7','8','9']
    operators = ['+','-','*','/','**','%']
    ui = ui.lower()    # change the case for any alpha character in the input
    check = ui.strip() # remove any spaces from the input and assign it to a new variable
    if check == 'q':   # a way out for the app
        return False
    for char in check: #loop through the check and exit the function if any of these fail
        if char not in digits\
           and char not in operators\
           and char != '.': return
        # if we get to this point, we know we have a valid input
        elif char.isdigit(): # do we have a digit?
            if not op:       # have we already had the operator?
                n1 += char   # if no, then it's a part of the first number
            else:            # if yes, then it's a part of the second number
                n2 += char
        elif char == '.':    # or it could be a decimal point
            if not op:       # then it's a part of the first number after the decimal point
                n1 += char
            else:            # otherwise it's a part of the second number after the decimal point
                n2 += char
        else:                # not a digit or a decimal point, it has to be the operator
            op = char
    if op and n1 and n2:     # if we have all the elements in place, pass them back
        return op, float(n1), float(n2)
There's quite a lot going on there, so I've commented the code, but if you don't understand anything, feel free to ask.

Now that we have a valid input and we know what the numbers are, and what the operation is, we can have a function to which that information can be passed:

def calculate(n1, n2, op):
    if op == "+":
        result = n1 + n2
    elif op == "-":
        result = n1 - n2
    elif op == "*":
        result = n1 * n2
    elif op == "/":
        result = n1 / n2
    elif op == "**":
        result = n1 ** n2
    elif op == "%":
        result = n1 % n2
    return result
Let's also give a user a little help, should it be needed:

def help():
    print("\nHelp:")
    print("Simply enter a calculation as\
    \n<number><operator><number>\
    \ne.g: 1+2 or 2*7 or 7/9\n")
All we now need is a 'driver' for the above functions:

print("=======<Calculator app>=======")
 
quit = False
while not quit: # the app will now run until quit is True
    calc = input("Input Q to quit: ")
    check = check_input(calc)
    if check:
        op = check[0]
        n1 = check[1]
        n2 = check[2]
        result = calculate(n1, n2, op)
        print(f"\nResult: {result}\n")
    elif check == None:
        help()
    else:
        print("All done.")
        quit = True
I hope that you can follow this mini tutorial and that you found it helpful. If you've any questions, simply ask, and I'm sure you'll get the answers.

Note: I've checked the above code and so far as I'm aware, there are no bugs; I'll correct any that are reported.

Also, if any more experienced coder can offer me any advice on what I've done here, then I'm open to that.

Thank you.



Bug reports and fixes.

Known bug: If more than one operator is entered, the result is incorrect.
Fix: Develop this app to handle that input. This can be done in ways that include...
1. Returning a error message for such an input and abort the calculation.
2. Have the additional operation saved to some register and applied to the final calculation.

Thank you so much. You have made me understood concepts I have struggled so hard to understand. I will continue to stay in touch. Once again, thank you. I really appreciate.
rob101 likes this post
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  a simple calculator Solstice 17 8,780 Mar-10-2019, 09:15 AM
Last Post: Ablazesphere

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020