4
\$\begingroup\$

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
\$\endgroup\$
3
  • \$\begingroup\$ Is your goal to reinvent-the-wheel? Because if not, 90% of your script code be replaced with one statement. \$\endgroup\$
    – AJNeufeld
    Commented 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
    Commented 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
    Commented Jun 11, 2020 at 13:30

2 Answers 2

3
\$\begingroup\$

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
\$\endgroup\$
7
  • \$\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\$
    – Peilonrayz
    Commented Jun 9, 2020 at 16:07
  • \$\begingroup\$ comments are inline \$\endgroup\$
    – d.j.yotta
    Commented 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
    Commented 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
    Commented 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
    Commented Jun 9, 2020 at 16:37
4
\$\begingroup\$

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, not negative_flag=False
  • no blanks after ( or before ), eg print(number_holder * -1) not print( 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 , 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


        
\$\endgroup\$

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.