Errors detected during execution are called exceptions and are not unconditionally fatal
It is Pythonic way to use them as much often as possible
Sometimes you can't predict all possible values and situations
No heaps of if else conditions
General form:
try:<code>except<ExceptionName>as<alias>:<code in case of error happened>else:<code if everything was OK>finally:<code inany case>
We are checking the code only after try before except.
🪄 Code:
defdivide_ten(num): try:print(10.0/float(num))exceptZeroDivisionErroras e:print(f"Oops, invalid - you can't pass 0. Error: {e}")exceptTypeError:print("Wrong type!", type(num))exceptExceptionas e:print("Unexpected exception:", e)else:print("Cool, no exceptions were triggered!")finally:print("We're done with that.")print("1)")divide_ten("2")print("2)")divide_ten(0)print("3)")divide_ten([3])
📟 Output:
1)
5.0
Cool, no exceptions were triggered!
We're done with that.
2)
Oops, invalid - you can't pass 0. Error: float division by zero
We're done with that.
3)
Wrong type! <class 'list'>
We're done with that.
Another working example:
Checking user's input and returning the list item. Non-ideal solution would be:
🪄 Code:
l =list(range(10))defget_item(): num =input("Enter index: ")# Fix this example!!!ifnot num.isdigit()or (num.startswith("-")andnot num[1:].isdigit()):return"Incorrect value"iflen(l)<int(num):return"Out of range"return l[int(num)]get_item()
📟 Output:
Enter index: -5
'Incorrect value'
Better solution:
defget_item(): num =input("Enter index: ")try:return l[int(num)]exceptIndexError:return"Out of range"except (TypeError,ValueError):return"Incorrect value"exceptExceptionas e:returnf"Unexpected exception: {e.__class__.__name__}({e})"
Custom Exceptions
classSimpleCustomException(Exception):passclassCustomException(Exception): def__init__(self,value="Some error"): self.parameter = value def__str__(self): return self.parameter
For raising custom Exception – raise used:
raise CustomException
raise CustomException()
🪄 Code:
# divide.py :classNegativeException(Exception): def__init__(self,message="Can't work with negative numbers"):#logger.error("...") self.message = messagedef__str__(self):return"ERROR: "+ self.messagedefdivide_ten(num):if num <0:raise NegativeExceptionreturn10.0/ num#========================# from divide import divide_ten, NegativeExceptiondefmain(num):try:print(divide_ten(num) )exceptZeroDivisionError:print("Oops, can't div by zero!")except NegativeException as e:print(e)main(0)main(-6)main(3)
📟 Output:
Oops, can't div by zero!
ERROR: Can't work with negative numbers
3.3333333333333335
Referring to exceptions
Please note that raise can also be used to print an error message and then re-raise the exception (allowing a caller to handle the exception as well):
🪄 Code:
import systry: f =open('/tmp/1123123.txt') s = f.readline() i =int(s.strip())exceptTypeError:print("Wrong type")exceptValueErroras e:print(f"Could not convert data to an integer: {e}")exceptOSErroras err:print("OS error: {}".format(err))print(err.filename)except:print("Unexpected error:", sys.exc_info())raise
📟 Output:
OS error: [Errno 2] No such file or directory: '/tmp/1123123.txt'
/tmp/1123123.txt
Catching multiple exceptions
Several except blocks
Putting several exceptions in a tuple
🪄 Code:
import systry: f =open('myfile.txt') s = f.readline() i =int(s.strip())except (IOError,ValueError) as e:print(f"Data or file error: {e}")
📟 Output:
Data or file error: [Errno 2] No such file or directory: 'myfile.txt'
__debug__ is special bool variable (interpreter-level constant (one of very few ones!)) which allways is True unless python is called with -O (optimization, capital O – not a zero!)
python –O script.py
Examples of assert:
🪄 Code:
assert1==1assertlen(range(5))==5try:assert2+2=="2 + 2","On paper it works..."exceptAssertionErroras e:print("Cought AssertionError:", e)