Posts: 10
Threads: 6
Joined: Aug 2022
Aug-18-2022, 02:24 PM
(This post was last modified: Aug-18-2022, 02:36 PM by Yoriz.
Edit Reason: Added code tags
)
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()
Posts: 453
Threads: 16
Joined: Jun 2022
Aug-18-2022, 03:38 PM
(This post was last modified: Aug-18-2022, 04:19 PM by rob101.
Edit Reason: typo
)
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
Posts: 10
Threads: 6
Joined: Aug 2022
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.
Posts: 453
Threads: 16
Joined: Jun 2022
Aug-18-2022, 04:42 PM
(This post was last modified: Aug-18-2022, 04:42 PM by rob101.
Edit Reason: typo
)
(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
Posts: 10
Threads: 6
Joined: Aug 2022
Aug-19-2022, 12:41 AM
(This post was last modified: Aug-19-2022, 12:41 AM by FelixLarry.)
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()
Posts: 1,145
Threads: 114
Joined: Sep 2019
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.
Posts: 453
Threads: 16
Joined: Jun 2022
Aug-19-2022, 10:07 AM
(This post was last modified: Aug-19-2022, 01:01 PM by rob101.
Edit Reason: typo / bug report
)
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
Posts: 1,950
Threads: 8
Joined: Jun 2018
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')
rob101 and FelixLarry 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.
Posts: 2,168
Threads: 35
Joined: Sep 2016
(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)):
Posts: 10
Threads: 6
Joined: Aug 2022
(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.
|