How to improve on this?

Hi all I am new to python and currently studying it at TAFE.

The below code is something I have wrote for an assignment (simple code to work out largest, smallest difference of list from user entered numbers)

Is there a way to format it better or use the try except in a better way.

I have tried to keep only the num_List function within the try except but can’t resolve the errors this produces without including all the other parts back.

import sys # This imports system so the standard error font is available.

while True:

try: # this will catch an error an error in the code between 'try' and 'except'.
    
    def num_List(): # This asks the user to enter 10 numbers.
        n = 1
        for n in range(5):
            print("Please enter number", n+1, ":", end = " ")
            num = int(input())
            lst.append(num)
            
    def small_Num(): # Returns the smallest number.
        return lst[-1]
        
    def large_Num(): # Returns the largest number.
        return lst[0]
        
    def diff():# Works out the difference between largest and smallest.
        return large_Num()-small_Num()

    lst = [] # The empty list.

    num_List() # Calls the num_List funtion.

    lst.sort(reverse = True) # sorts the list in decending order.

except ValueError:
    print("That was not a number!", file=sys.stderr)

else:
    # Prints the results in a red font and calls the large_Num(), small_Num() and diff() functions.
    print("\n\nThe number List is", lst, "\n\nThe large number is ", large_Num(),
          "\n\nThe small number is ", small_Num(),"\n\nThe difference between",
          large_Num(), "and", small_Num(), "is", diff())
    
    print('\n\nThats All Folks!')
    break # ends the loop once it completes successfully.

Look up the functions max and min

The Python docs are the best friend you’ll ever have if you’re going to be coding. It’s worth getting to know them well.

What you’re doing here:

def num_List(): # This asks the user to enter 10 numbers.
    n = 1
    for n in range(5):
        print("Please enter number", n+1, ":", end = " ")
        num = int(input())
        lst.append(num)

lst = [] # The empty list.
num_List() # Calls the num_List funtion.

is a lot more complicated than you realize, and a Very Bad Idea unless you know what you’re doing. You’re manipulating an object from an enclosing scope from within a function. It works, but if you changed lst.append(num) to lst += [num] it doesn’t. The reason why involves the difference between mutable and immutable objects, local and nonlocal variables, and the different ways variables are loaded in cpython bytecode (LOAD_FAST vs LOAD_DEREF).

Don’t worry about any of that at this point in your learning process. Instead, only use variables within the scope where they were first defined. If you need to use a certain variable in another scope, pass it as a function argument and return the result:

def num_List():
    lst = []
    for n in range(5):
        print("Please enter number", n+1, ":", end = " ")
        num = int(input())
        lst.append(num)
    return lst

lst = num_List()

This is both safer and more readable.

Additionally:

  • Avoid defining functions inside loops. Put them at the top of your module (a module is a .py file).
  • Try to keep try blocks as small as possible. Doing so helps readability and reduces the risk that something unexpected goes wrong.
  • Your comment says that num_List asks for ten numbers, but it actually only asks for five.
  • You don’t need to define n before the for loop.
  • You don’t need to use print, instead put the instruction string directly in input:
    input(f"Please enter number {n+1}: ")
    (This method of putting variables in strings is called an f-string, they’re pretty useful.)

Thanks Stephen we are not up to max/min yet so just trying to stay within the scope of what has been taught

Thanks Alexander this is really good feed back, dont understand the first part fully yet but will keep reading and hopefully it will make sense soon. That f string is nice, have added it to my research list thank you again very helpful

There is rarely need to put a function definition inside a try block. Usually there are no exceptions you would like to catch during a function definition.

Hi thanks Vaclav. Without the try block how would I go about catching a ValueType error such as if someone inputs a letter? The aim was to make the code run until they successfully entered all the numbers

Václav isn’t saying not to use a try block at all, just that you shouldn’t define your functions inside it. Put the function definitions at the top of your file, before the try.

2 Likes

Ah ok. I have tried just putting the try block around the code where the input is taken but it just throws errors and every time I fix that error another one pops up :joy:

And by the time I have fixed them all the code is back to how it was :cry:

Well think I figured it out, a lot smaller footprint and hopefully easier to read

import sys # This imports system so the standard error font is available.


lst = []
n = 1

while True:
    try:
        for n in range(10):
            num = int(input(f"Please enter number {n+1} :"))
            lst.append(num)
            lst.sort(reverse = True)
    except ValueError:
        print('That was not a number', file=sys.stderr)
    else:
        print('\n\nThis is the list', lst, '\n\nThe largest number', max(lst),
              '\n\nThe smallest number', min(lst), '\n\nThe difference', max(lst)-min(lst))
        break

If I read this code correctly then every time ValueError is raised then code starts asking 10 numbers all over again while keeping the previously entered numbers in lst. Is this desired behavior?

1 Like

In your case applying exception catching regarding str to int conversion in this code:

Would look like this:

            input_text = input(f"Please enter number {n+1} :")
            try:
                num = int(input_text)
            except ValueError:
                ...

Catch only exceptions you really want to catch. By not following this advice you are hiding possible errors making them much harder to discover.

Hi Aivar that’s about as much as I know :blush: my intention was to look at restarting it from where it left off I.e if they entered 9 correct and one wrong character it would restart from number 9 keeping the list and the user only having to enter 1 more number again. Wasn’t really sure where to start with that though.

Hi Vaclav thank you I thought you had to have the whole loop inside the try block didn’t know you could just place it around the actual input will sit down later today and re try it :blush:

What Vaclac’s getting at is that usually you should put the try/except
around the smallest piece of code you can, because that way you know
that whatever exception you’re catching came from that code and not from
something unrelated.

His example code put the try/except around just the int() call. That
way you’re handling a ValueError purely from trying to convert the
user’s input into an int. That is the only error you want to handle.

All the other code should run without errors, so if a ValueError
(or other exception) arises you want to see it happen so that you can
debug the mistake, not mistakenly catch it and hide it because you
thought it was user error.

The basic rules of thumb are two:

  • put try/except around exactly what you’re handling and not other code
  • catch only the exceptions you expect, and nothing else

You’re already doing the second bit, but a lot of new programmers might
write:

 try:
     value = int(input_string)
 except:
     print("bad input!")
     continue

or something like that, which catches any exception, including
programmer errors. Example:

 input_string = input("Enter something: ")
 try:
     value = int(inpu_string)
 except:
     print("bad input!")
     continue

because the example isspells input_string as inpu_string in the
int() call, the code would raise a NameError. Because the exception
catches anything, this programmer error would be hidden, and treated
like a user error.

Cheers,
Cameron Simpson cs@cskk.id.au

1 Like

Hi Cameron thanks for taking the time and explaining the try block. I spent some time on it last night to try and catch the error at the user in put but to avail.

I dont have any other ideas on where to put the block as if i wrap it around the

try:
user_num = int(input_text) 
except ValueError:

It breaks the FOR loop, making it run the whole code and then ask for the next number.
I am thinking i have got the ELSE: part of the code in the wrong place or something


lst = []
n = 1

while True:
        for n in range(10):
            num = int(input(f"Please enter number {n+1} :"))
            try:
                user_input = int(num)
            except ValueError:
                print("That was not a number!")
            lst.append(num)
            lst.sort(reverse = True)
            else:
                print('\n\nThis is the list', lst, '\n\nThe largest number', max(lst),
                      '\n\nThe smallest number', min(lst), '\n\nThe difference', max(lst)-min(lst))
                break

This try will give me an error because the lst.append(num) and the lst.sort

I have tried moving them around, including them in the except block or putting it out of the for loop. but everything throws and error or breaks the for loop.

Any suggestions would be gratefully received

Ok think i found it, I had the try block in the FOR loop

import sys # This imports system so the standard error font is available.


lst = []
n = 1

while True:
        for n in range(10):
            num = int(input(f"Please enter number {n+1} :"))
            lst.append(num)
            lst.sort(reverse = True)
        try:
            user_input = int(num)
        except ValueError:
            print("That was not a number!")
        else:
            print('\n\nThis is the list', lst, '\n\nThe largest number', max(lst),
                  '\n\nThe smallest number', min(lst), '\n\nThe difference', max(lst)-min(lst))
            break
        

Bah nope lol

now its not catching a ValueError :confounded:

import sys # This imports system so the standard error font is available.

I assume this is local to your environment in some way.

lst =
n = 1
while True:
for n in range(10):
num = int(input(f"Please enter number {n+1} :"))

You don’t have a try/except around the int() in the line above.

       lst.append(num)
       lst.sort(reverse = True)
   try:
       user_input = int(num)
   except ValueError:
       print("That was not a number!")
   else:
       print('\n\nThis is the list', lst, '\n\nThe largest number', max(lst),
             '\n\nThe smallest number', min(lst), '\n\nThe difference', max(lst)-min(lst))
       break

This is good, but too late. Something shaped like this:

 num_s = input(f"Please enter number {n+1} :")
 try:
     num = int(num_s)
 except ValueError:
     print("That was not a number!")
 else:
     print("This is the list", lst)
     print("The largest number", max(lst))
     print("The smallest number", min(lst))
     print('The difference', max(lst)-min(lst))
     break

Cheers,
Cameron Simpson cs@cskk.id.au

Have a look at these two solutions

#!python3.10
#~ numbers=[5,2,5,2,2]
#~ for item in numbers:
    #~ stars = [""]
    #~ for stuff in range(item):
        #~ stars+="*"
    #~ print('F', stars)

#~ numbers = [3,6,2,4,8,10]
#~ max=numbers[0]
#~ for item in numbers:
    #~ if item>max:
        #~ max=item
#~ print('Max', max)


import sys # This imports system so the standard error font is available.


lst = []
n = 1

for n in range(3):
    repeat = True
    while repeat:
        num = input(f"Please enter number {n+1} :")
        try:
            num = int(num)
            repeat = False
        except ValueError:
                print("That was not a number!")
    lst.append(num)
    lst.sort(reverse = True)
print(type(lst), type(lst[0]))
print('\n\nThis is the list', lst, '\n\nThe largest number', max(lst),
      '\n\nThe smallest number', min(lst), '\n\nThe difference', max(lst)-min(lst))

# .................................................
lst = []

def get(n):
    while True:
        text = input(f"Please enter number {n} : ")
        try:
            num = int(text)
            return num
        except ValueError:
            print(F'{text} is not a number')


for n in range(1, 4):
        num = get(n)
        lst.append(num)
lst.sort(reverse = True)
print(type(lst), type(lst[0]))
print('\n\nThis is the list', lst, '\n\nThe largest number', max(lst),
      '\n\nThe smallest number', min(lst), '\n\nThe difference', max(lst)-min(lst))

John

Hi John thanks for taking the time to show me this, it was driving me crazy!

I see where I was going wrong now… don’t think I would have figured it out at this stage either.

The last for loop is good for me to see as well, as I was wondering how to lose the n = 1 variable at the top. I now remember seeing the lecturer show us that in class and I was thinking at the time why would I ever need that!! :smiley:

It’s not until it is put in something that resembles a program that you realise that it is useful…

Also, that is a very handy thing to do with the WHILE loop placing a variable as the True, I haven’t come across that one either.

Just to make sure im reading it right, while it is TRUE it asks the user for a number then tests the user input in the try block, when that is good it the sets the repeat to false which then exits the loop and appends num to the list, then goes back to the for loop and repeats it again until the range() has been met?