I get what it does but I don't understand why we do it? Why are we trying to except something?
For example:
for i, value in enumerate(arr):
try:
result_dict[value] += 1
except KeyError:
result_dict[value] = 1
Why do I have to do except KeyError? I don't know why a KeyError is thrown. Why can't I just do
result_dict[value] += 1
in the first place?
What goes inside the except block for any try/catch? I get the error has to be thrown but what condition has to do inside the except block?
Sorry if my question is too dumb. I'm a beginner.
TLDR
try/except blocks makes sure that your program continues running rather than ending abruptly. By including a except KeyError, we are specifically making sure that the program does not end abruptly if a KeyError occurs. result_dict[value] += 1 will throw an error if value is not a key in the dictionary, because it tries to do access a key that does not exist. The += makes the code run similarly to:
result_dict[value] = result_dict[value] + 1
and since value is not in result_dict, it is similar to saying
result_dict[value] = None + 1
which is bad.
The Non-TLDR version
When an error occurs in python, the program usually terminates abruptly and exits. It does not run any code that occurs below the part where the exception occurs. For example, take the following code. It takes 2 numbers from the user a and b, and it will output a/b
a = int(input("Enter a value for a: "))
b = int(input("Enter a value for b: "))
print("a/b is:", a/b)
If the user gives a valid input (say a=4, b=2) the program proceeds smoothly. However if the user were to give, say a = "c", then the following happens
Traceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'c'
And the program ends abruptly. It is perfectly valid to have the program end like that, but rarely do you want the program to end abruptly. Take the case where the user has 1000 inputs, and the last one messes up. The user will then have to restart the program and re-input all 1000 inputs again, because the program ended abruptly.
So now we introduce a try/except block. We know that converting a non-numeric character into an integer will throw a ValueError as seen in the error, so we will handle them accordingly
while True:
try:
a = int(input("Enter a value for a: "))
break
except:
print("An error has occurred. Please input a again")
while True:
try:
b = int(input("Enter a value for b: "))
break
except:
print("An error has occurred. Please input b again")
print("a/b is:", a/b)
So now, we take an input from the user, try to convert it into an integer and put that value into a. If it succeeds, it will proceed smoothly to the next line (break) and exit the loop. If it fails, then an exception will occur, and it will enter the except block, where it will print a helpful error message, and run the loop again (until the user enters a valid input). Now the program doesn't just terminate abruptly when it fails. It gives appropriate feedback to the user, and lets the user retry the input again.
That is a general exception block, where you just catch any exception.
But let's now say that there are multiple possible errors that could occur. Take the following code:
a = input()
b = input()
print(int(a)/int(b))
print("I am done")
Now a few errors can occur. One of them is the ValueError stated above, where the input given cannot be converted into an integer. The other error, is a ZeroDivisionError where b=0. Python doesn't like dividing by zero, hence it will throw an exception and terminate the program immediately.
So now, you want to print a special message for each type of program. How you do that is by catching specific exceptions
a = input("Enter a value for a: ")
b = input("Enter a value for b: ")
try:
print(int(a)/int(b))
except ValueError:
print("We cannot convert a non-numeric character to an integer")
except ZeroDivisionError:
print("We cannot divide by 0")
except:
print("Some unexpected error has occurred")
print("I am done")
If python was unable to convert the input into an integer, it will enter the ValueError block of the code, and print "We cannot convert a non-numeric character to an integer"
If python tried to divide by 0, then it will enter the ZeroDivisionError block and print "We cannot divide by 0"
If any other type of error occurred, it will end up in the final except block and print "Some unexpected error has occurred"
And after the exception is handled, it prints "I am done"
Note that if the final except block was not there to catch any other exceptions, then the program will not terminate nicely because the exception was unhandled, and it will not print the final statement.
Now, how does one realise what possible error can occur? That is up to practice, and once you get familiar with the errors, you can handle them accordingly. Or, purposefully make your program throw exceptions, and read the stack trace for what kind of exceptions occur. And then handle them accordingly. You don't have to handle each specific error differently, you could handle all of them in the same way.
You can read more here: Python's documentation for errors and exceptions
Here's why. You cant add to something that is None. For example say var1 = None. I couldn't do something like var = var + 1. That would throw an error. In the example you shared the code will check if you can add 1 to value's key. If you cannot do this you assign a value to the key.
for i, value in enumerate(arr):
try:
#Try and add 1 to a dict key's value.
result_dict[value] += 1
except KeyError:
#Say we get an error we need to assign the dict key's value to 1.
result_dict[value] = 1
A KeyError will crash/terminate your program if result_dict[value] doesn't exist. By using an except block, you tell the program what to do in the event of a KeyError (here, assign result_dict[value] = 1 if it doesn't exist), and then the program continues. Basically, you're avoiding a crash.
Let's say your value is "banana". If you don't have a result_dict["banana"], you can't add 1 to nothing, i.e. None += 1.
By using except KeyError, you intercept the error before it stops your program, and now you have set a key-value pair for your result_dict, rather than terminating the program.
Let's start with an example
We have tenants in an apartment building who can reserve parking spots. Some tenants have no car and won't have a parking spot, but only tenants can reserve a spot. We track parking spots in a dictionary:
parking_spots = { "alice": 1, "bob": 2, "chris": 0 }
Chris doesn't have spots because he walks to work.
What should happen if Eve tries to reserve a spot?
parking_spots["eve"]
That code asks "how many spots has eve reserved?" However, another question this answers is whether Eve is a tenant of the building at all. Python represents this by having parking_spots["eve"] throw a KeyError which is not like any other value. If python didn't do this and returned 0 by default, then parking_spots["eve"] and parking_spots["chris"] would look the same.
But wait, I know that this particular dictionary isn't being used that way
Cool, that's pretty common. It's so common in fact that there are multiple ways of doing this.
Instead of
result_dict = {}
for i, value in enumerate(arr):
try:
result_dict[value] += 1
except KeyError:
result_dict[value] = 1
You can use
result_dict = {}
result_dict.setdefault(0)
result_dict += 1
Or you can use defaultdict.
from collections import defaultdict
result_dict = defaultdict(int)
result_dict += 1
Some CS Theory
There are two theoretical concepts that we could talk about here:
Exceptions as a control flow mechanism
The api of a dictionary
Exceptions for Control Flow
try/catch is style of control flow. It is simply a nicer way to think of code for certain cases. It is almost never required as long as you can use if/else, but is many times the best way to think about code.
Depending on who you talk to, exception handling can be a somewhat controversial feature:
C++ - Arguments for Exceptions over Return Codes
In my opinion, it's rare that exceptions are truly the right answer if you design a system from scratch. Generally, I prefer using an Option (or more generally sum types).
However, in many cases, they are the best answer given you starting constraints.
Dictionary API
Python throwing an exception when the key isn't present is a design choice and not the only way the API could be written. Instead, it could simply return None. Many languages follow the latter approach as they consider it more safe - especially when you work in a modern typed language like Rust, Haskell, Elm, etc.
Further Reading
I would also encourage you to read Amp's answer as it covers some other details on the particulars of exception handling that may be instructive.
I have this piece of code here:
money_to_dep = int(input(f"\nHow much money do you want to deposit?(enter amount ONLY):\n>> "))
while True:
try:
print(money_to_dep)
break
except ValueError:
print(f"Wrong INPUT. Try again.")
break
When I type a string instead of an integer, an error still occurs even though I have done except ValueError? Why does this happen? This question really annoys me as I have never been able to have a clear understanding as to how try-except works with handling errors.
You have to put your code like this:
while True:
try:
money_to_dep = int(input(f"\nHow much money do you want to deposit?(enter amount ONLY):\n>> "))
print(money_to_dep)
break
except ValueError:
print(f"Wrong INPUT. Try again.")
The ValueError raise when you use int. So it has to be inside the try statement.
This means that your exception is occuring before your try catch. you need to put your input code inside try, so it can catch exception.
i.e
money_to_dep = int(input(f"\nHow much money do you want to deposit?(enter amount ONLY):\n>> "))
Exveption occurs, but not in try block so not catched. Change it to
while True:
try:
money_to_dep = int(input(f"\nHow much money do you want to deposit?(enter amount ONLY):\n>> "))
print(money_to_dep)
break
except Exception as E:
print(f"Wrong INPUT. Try again.")
break
I'm trying to write a basic tic-tac-toe in Python. One of my functions is meant to get the user's input as a number from 1 to 9. If the user enters a non-integer, or a number not between 1 and 9, it will return an error. It's nestled in a try-except to avoid worrying about type conversion.
def get_move():
print("Your move ...")
while True:
try:
move = input("Type a number from 1-9: ")
if move.isnumeric():
if (0 < int(move) < 10):
break
else:
print("The number is outside the range. Try again.")
else:
if (move.tolower == "quit") or (move.tolower == "exit"):
exit()
except:
print("Not a valid integer, try again.")
return move
This sort-of works when I run normally. But when I try to debug in VS Code, when the debugger reaches the line move = input("Type a number from 1-9: ") and I click "Step Into", it simply goes straight to the "except" clause. And the code proceeds in an infinite loop - it will never stop and wait for the user input, meaning that I have to manually stop the debugger.
Any idea why it might be doing this?
Edit:
Thanks for correcting my typo, but that hasn't solved the problem. I've changed the line from:
if (move.tolower == "quit") or (move.tolower == "exit"):
exit()
to:
if move.lower() == "quit" or move.lower() == "exit":
exit()
And also changed the except clause to except (ValueError, TypeError). Now I receive the following error:
Exception has occurred: EOFError
EOF when reading a line
File "[...]tictacpy.py", line 18, in get_move
move = input("Type a number from 1-9: ")
File "[...]tictacpy.py", line 46, in <module>
move = get_move()
Most likely this is happening because in the debugger you do not have a console for standard input, so calling input() will error (I don't know VS code specifically so I'm guessing here, but this is a reasonable cause).
In any case, I'd strongly suggest not using all-catching except clauses as this silences and wrongly handles errors that may happen that are not a part of your expected flow.
I'd start by changing your except to say except (ValueError, TypeError) so that it only catches errors resulting from bad input / type conversion issues. You'll then be able to see what the real error is.
Also, note that there is no such thing as move.tolower - you probably meant move.lower(). Maybe that's your bug?
In my program, I want to take in an input which should be a number.If the user inputs a string, however, the program returns an exception.I want the program set in such a way that the input is converted to int and then if the string is anything other than an int the program prints that "Stop writing please".Like for example:
x=input("Enter a number")
if int(x)=?: #This line should check whether the integer conversion is possible
print("YES")
else #This line should execute if the above conversion couldn't take place
print("Stop writing stuff")
You'll need to use try-except blocks:
x=input("Enter a number")
try:
x = int(x) # If the int conversion fails, the program jumps to the exception
print("YES") # In that case, this line will not be reached
except ValueError:
print("Stop writing stuff")
You can simply use a try-except block to catch the exceptional case, and inside there, print your statement. Something like this:
x=input("Enter a number")
try:
x=int(x)
print("YES")
except:
print("Stop writing stuff")
I have a piece of code.
import sys
while(True):
print "Enter a number: "
try:
number = int(sys.stdin.readline())
except ValueError:
print "Error! Enter again an integer value"
continue
finally:
print number
break
Here I expect when I enter a non-integer number, the output should be
Error! Enter again an integer value
and then it should ask for input. But it is printing the message but asking for further inputs. Please explain it or if am thinking it wrong.
If I handle with NameError, then error message is not even being printed and the program is exiting with a traceback call.
The finally clause always runs, whether an exception was caught or not. You want else, which runs when there was no exception.
Also: you don't need parentheses for a while, and you probably want the raw_input function which is a little nicer to use than messing with sys.stdin directly.
So I would do:
while True:
try:
number = int(raw_input("Enter a number: "))
except ValueError:
print "Error! Enter again an integer value"
continue
else:
print number
break
Your finally should be else, otherwise it will execute regardless of whether or not there was an exception.