I have made a script to convert number from any base to decimal.
I am trying to optimize the code because I think it is bloated and can be improved but if I change anything it affects error handling negatively. I tried removing the two while loops but then the user would need to enter their number again even if they only messed up their base that is something I want to avoid.
I think printing and handling the number can be optimized but I just cannot see how. Also if anyone can help me improve my code readability that would be helpful too.
while True: #Main loop will keep the script running until user exits
negative_flag=False #flag to check if a value is negative or not. False by default
while True: # Loop made so that any error can be handled if input is not number
number=input("Enter a number : ")
try:
if type(int(number))==int:
break
except:
print("Wrong input. Only integers are allowed.")
continue
while True: # Loop made so that any error can be handled if input is not number
base_from=input("Enter the base number is in : ")
try:
if type(int(base_from))==int and int(base_from)>=2:
break
else:
print("Base should be greater than or equal to 2")
except:
print("Wrong input. Only integers are allowed.")
continue
if int(number)<0: #Check if a number is negative if it is convert it into a postive
(number)=int(number)*-1
negative_flag=True
number=str(number)
#Not useful just for reference base_to=10
number_size=len(number)
number_holder=0
for x in number: #Basic conversion of a number to decimal number system.
number_size-=1
base_raised=int(base_from)**number_size
number_multiplied=base_raised*int(x)
number_holder=number_holder+number_multiplied
if negative_flag==True: # Prints if the value is -ve
print( number_holder*-1 )
else:
print(number_holder)
while True: # Final loop asking users if they want to exit or not
response=input("Do you want to continue?(y or n) - ")
if response=='n' or response=='y':
break
else:
print("Wrong input")
continue
if response == 'n': # Conditions for only two valid answer.
break
elif response == 'y':
continue
-
\$\begingroup\$ Is your goal to reinvent-the-wheel? Because if not, 90% of your script code be replaced with one statement. \$\endgroup\$AJNeufeld– AJNeufeld2020年06月09日 18:51:01 +00:00Commented Jun 9, 2020 at 18:51
-
\$\begingroup\$ @AJNeufeld I am just trying to learn. I know the functions already exist, doesn't mean I can't try to recreate them. I used to program in C so i am just trying to replicate as much code as I can in python, then start using the inbuilt functions once I get the hang of the language. \$\endgroup\$Harvey– Harvey2020年06月11日 06:05:58 +00:00Commented Jun 11, 2020 at 6:05
-
\$\begingroup\$ That's perfectly valid. If you know the function exists, but still want to implement it yourself, for practice or a better understanding of the inner workings, we call that "reinventing the wheel". Tagging the question with that tag is a signal for reviewers to ignore the "just replace this lump of code with this one function call" quick review option and instead provide more feedback on the "lump of code" itself. I'll add the tag for you, then I'll have to update my answer post accordingly. \$\endgroup\$AJNeufeld– AJNeufeld2020年06月11日 13:30:55 +00:00Commented Jun 11, 2020 at 13:30
2 Answers 2
Typically, you have a single loop while user is doing input
Use try/except to handle loop flow. The order you get things makes a difference as the next input's correctness may depend on previous input.
Separate out your steps into methods
There are three distinct parts to your flow, loop could be simplified by calling your methods one by one.
Indentation is important
My solution is untested (on my phone), but should give you a base to work off.
More comments inline
# you should only need one loop for stuff like this...
number = None
base_from = None
base_to = 10 # this is implied by your code. You could allow user to specify this too...
while True: #Main loop will keep the script running until user exits
negative_flag=False #flag to check if a value is negative or not. False by default
base_from=input("Enter the base number is in : ") if base_from is None else base_from # don't get this if we have it already
try:
base_from = int(base_from) # just want an int
if base_from <= 1:
raise ValueError("Base should be greater than or equal to 2")
except ValueError as e: # two types of ValueError, one is not int, other is less than 2
base_from = None # need to clear this to get it again as current value is not ok
print("Wrong input. {}".format(e)) # you can make the messages friendlier yourself
continue
number=input("Enter a number : ") if number is None else number # keep this check simple. Ensure number is either None or set correctly
try:
# of course this only works for bases less than or equal to 10 as you haven't specified the notation for higher bases...
result = 0
for char in number:
if char == '-':
negative_flag = True
continue
result *= base_from # mathematically identical to 'shift left' in specified base
digit = int(char) # int() raises ValueError if char is not int
if not (0<=digit<base_from):
raise ValueError("digits must be between 0 and {} for base {}".format(base_from-1,base_from))
# digit must be ok...
result += digit
except ValueError as e: # only catch specific exception... we don't know how to (and shouldn't) handle other exceptions here
number = None # need to reset this to start over as the number is not ok
print("Wrong input. {}".format(e))
continue
print(-result if negative_flag else result)
# this one could be a separate loop depending how fancy you need it. But if that is the case, should be a separate method and call it and either break or continue based on return value... shouldn't throw exceptions as that will be difficult to catch here
response=input("Do you want to continue?(y or n) - ")
if response.lower().startswith('n'):
break
else:
base_from = None
number = None
continue
-
\$\begingroup\$ Answers on Code Review must include at least one insightful observation in the body of the post. Posting only a code dump with no further explanation goes against the point of Code Review which is to teach users to become better programmers. Rather than give users a fish that only lasts a day. Please edit your post to address this problem. \$\endgroup\$2020年06月09日 16:07:33 +00:00Commented Jun 9, 2020 at 16:07
-
\$\begingroup\$ comments are inline \$\endgroup\$d.j.yotta– d.j.yotta2020年06月09日 16:10:50 +00:00Commented Jun 9, 2020 at 16:10
-
\$\begingroup\$ Can you clarify the use of None in 'if base_from is None' .I know it is sort of null value but this is my first program in python so i am not sure about it. \$\endgroup\$Harvey– Harvey2020年06月09日 16:17:54 +00:00Commented Jun 9, 2020 at 16:17
-
\$\begingroup\$ @arsh you're right, it's specifically setting it to None to indicate it is not set. There is a difference between setting to None and not declaring at all. If I left undeclared, it would be easy to hit a "name 'basefrom' dose not exist" - which is often hard to figure out why. However, setting to None is not always the right thing to do, better to leave undeclared in the case you do want things to break if names don't exist when you expect them to be. Here, we are looping until we get all the bits we need from the user, so we use None to indicate what we still need to get. \$\endgroup\$d.j.yotta– d.j.yotta2020年06月09日 16:23:42 +00:00Commented Jun 9, 2020 at 16:23
-
\$\begingroup\$ Alright so basically we are checking if the value is Null or not. If it is, we take the input from the user and if not then we don't. Replacing the need for two while loops. Is that right? \$\endgroup\$Harvey– Harvey2020年06月09日 16:37:49 +00:00Commented Jun 9, 2020 at 16:37
PEP-8
The Style Guide for Python Code has many recommendations for the formatting of Python code that all developers should (must!) follow. These include
- single blank space around binary operators, eg
negative_flag = False
, notnegative_flag=False
- no blanks after
(
or before)
, egprint(number_holder * -1)
notprint( number_holder*-1 )
Useless code
int(number)
can only ever return an int
, or raise an exception. There are no other possibilities. So if no exception is raised, the if
condition will always be true, making the if
statement an unnecessary control structure:
try:
if type(int(number)) == int:
...
except:
...
Similarly you only exit this loop if response == 'n' or response == 'y'
while True:
response = input("Do you want to continue?(y or n) - ")
if response=='n' or response=='y':
break
else:
print("Wrong input")
continue
So why test both possibilities?
if response == 'n': # Conditions for only two valid answer.
break
elif response == 'y':
continue
From the comment, it seems you have already realized this. So why elif response == 'y':
? Why not simple else:
?
Finally, every last one of the continue
statements is used as the last statement in a control structure, in a loop. Without the continue
statement, the loop would be restarting anyway, so these can all be omitted.
Only catch the exceptions you expect
Consider this code:
while True:
number = input("Enter a number : ")
try:
if type(int(number)) == int:
break
except:
print("Wrong input. Only integers are allowed.")
continue
Try pressing Control-C
at the input prompt to quit the program. Whoops! Why isn't the program terminating? Because Control-C
raises the KeyboardInterrupt
exception, which you catch, display an inappropriate error messages, and then loop back for another try. How unexpected.
You want to catch ValueError
exceptions, only.
while True:
try:
number = int(input("Enter a number: "))
break
except ValueError:
print("Wrong input. Only integers are allowed.")
Note the absence of useless if
statements and continue
statements.
Any Base?
Your program is supposed to convert from "any base" to decimal. But really, you only allow base 2 through base 10 input. You don't allow FF
to be converted from base 16 to base 10.
To Base 10
You've got a lot of code to convert a number to base 10. This functionality is built-in to Python. If your intention is not reinventing-the-wheel, you should use the int(x, base=10)
function.
Reworked Code
while True:
number = input("Enter a number: ")
while True:
try:
base = int(input("Enter the base the number is in: "))
if base >= 2:
break
print("Base should be greater than or equal to 2")
except ValueError:
print("Wrong input. Only integers are allowed")
try:
print(int(number, base))
except ValueError:
print(f"Unable to convert {number!r} to base {base}")
while True:
response = input("Do you want to continue? (y or n): ")
if response in {'y', 'n'}:
break
print("Wrong input")
if response == 'n':
break
Or super condensed:
print("Press Control-C to stop.")
while True:
try:
print(int(input("Number: "), int(input("Base: "))))
except ValueError as err:
print("Invalid input:", err)
except KeyboardInterrupt:
print("Conversions to base-10 complete.")
break
Explore related questions
See similar questions with these tags.