Syntax Errors
-------------
- parsing errors
- common errors when you are still learning python
>>> while True print('Hello world')
File "<stdin>", line 1
while True print('Hello world')
^
SyntaxError: invalid syntax
Exceptions
----------
- errors occured during execution
- types:
a. built-in exceptions
b. user-definedexceptions
- can be handled
Examples of Built-in exceptions | >>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't convert 'int' object to str implicitly |
EOFError | - means you hit CTRL + D - useful in input() and raw_input() |
Handling Exceptions
-------------------
- exceptions can be handled use `try .. except` clause
- execution is as follows:
* First, the try clause (the statement(s) between the try and except
keywords) is executed.
* If no exception occurs, the except clause is skipped and execution of the
try statement is finished.
* If an exception occurs during execution of the try clause, the rest of
that try clause is skipped. Then if its type matches the exception named
after the except keyword, the except clause is executed, and then
execution continues after the try statement (jumping to another except or
finally clause).
* If an exception occurs which does not match the exception named in the
except clause, it is passed or to outer try statements; if no handler is
found, it is an unhandled exception and execution stops with a message
- `try` statement can have multiple `except` clause or handlers
- a handler is the clause within `except` statement
* handles exceptions that occure in the corresponding `try` statement
* doesn't handle exceptions generated on other handlers within same `try`
statement
Basic `try .. except` clause | >>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ... |
Multiple exceptions | ... except (RuntimeError, TypeError, NameError): ... pass |
Class in except clause | A class in an except clause is compatible with an exception if it is the same class or a base class thereof (but not the other way around — an except clause listing a derived class is not compatible with a base class). For example, the following code will print B, C, D in that order:
class B(Exception): pass
class C(B): pass
class D(C): pass
for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B")
Note that if the except clauses were reversed (with except B first), it would have printed B, B, B — the first matching except clause is triggered. |
`except` for everything else | A last `except` clause may serve as a wildcard. import sys
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise |
Using `else` clause in `try` | An optional `else` statement can follow a `try` statement. The `else` clause for arg in sys.argv[1:]: try: f = open(arg, 'r') except OSError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close() |
Exception arguments | `except` clause may store the exception into a variable. raise Exception('spam', 'eggs') except Exception as inst: print(type(inst)) # the exception instance print(inst.args) # arguments stored in .args print(inst) # __str__ allows args to be printed directly, # but may be overridden in exception subclasses x, y = inst.args # unpack args x, y = inst # unpack args print('x =', x) print('y =', y) |
Exceptions inside functions | Exceptions can also be handled even they are generated from inside a function. >>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as err: ... print('Handling run-time error:', err) ... Handling run-time error: division by zero |
Raising Exceptions
------------------
- allows programmers to force an exception to occur
- syntax: `raise ExceptionName`
- types of ExceptionNames:
a. exception instance
b. exception class (class derived from an exception)
Raising an exception | >>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: HiThere |
Using exception class | raise ValueError # shorthand for 'raise ValueError()' |
Re-raising an exception | This can be used if you want to determine whether an exception occured but you dont't want to >>> try: ... raise NameError('HiThere') ... except NameError: ... print('An exception flew by!') ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in <module> NameError: HiThere |
User-defined Exceptions
-----------------------
- usually derived from `Exception` class
- common practice:
1. create a base class for that module
2. create subclasses for different error conditions
- standard naming convention appending "Error" at the exception name
Using a user-defined exception | This program asks the user for a number. If the number is smaller than, it will raise # define Python user-defined exceptions class Error(Exception): """Base class for other exceptions""" pass
class ValueTooSmallError(Error): """Raised when the input value is too small""" pass
class ValueTooLargeError(Error): """Raised when the input value is too large""" pass
# our main program # user guesses a number until he/she gets it right
# you need to guess this number number = 10
while True: try: i_num = int(input("Enter a number: ")) if i_num < number: raise ValueTooSmallError elif i_num > number: raise ValueTooLargeError break except ValueTooSmallError: print("This value is too small, try again!") print() except ValueTooLargeError: print("This value is too large, try again!") print()
print("Congratulations! You guessed it correctly.")
Here is a sample output of the program: Enter a number: 12 This value is too large, try again!
Enter a number: 0 This value is too small, try again!
Enter a number: 8 This value is too small, try again!
Enter a number: 10 Congratulations! You guessed it correctly. |
Clean-up actions: `finally` statement
-------------------------------------
- `finally` statement allows to execute actions regardless of the outcome of
`try` clause
- when does `finally` clause run?
* when there is no exception
-> `try` executes
-> `finally` executes
* when there is a handled exception
-> exception is handled
-> `finally` executes
* when there is an unhandled exception
-> `finally` executes
-> exception is re-reaised
* when `try` ends in a break, continue, etc..
- uses:
a. enables programs that must free up resources regardless of the
outcome of the main code (e.g network connections, files, etc.)
Simple demo | >>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print("division by zero!") ... else: ... print("result is", result) ... finally: ... print("executing finally clause") ... >>> divide(2, 1) result is 2.0 executing finally clause >>> divide(2, 0) division by zero! executing finally clause >>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in divide TypeError: unsupported operand type(s) for /: 'str' and 'str' |
Another example | The `with` statement can be used to open and close a file regardless on what action you have done on the file object. for line in open("myfile.txt"): print(line, end="") |
Assertions
----------
- way of testing a condition
* if true, returns nothing and program continues
* if false, it raises an "AssertionError"
- uses:
a. used to trace a bug in a program (internal self check)
b. informs programmer about unrecoverable errors
- not used to catch an error and handle it
Assertion in action | As an example, this function computes the final price of a product after applying a def apply_discount(product, discount): price = int(product['price'] * (1.0 - discount)) assert 0 <= price <= product['price'] return price # # Our example product: Nice shoes for $149.00 # >>> shoes = {'name': 'Fancy Shoes', 'price': 14900} # # 25% off -> $111.75 # >>> apply_discount(shoes, 0.25) 11175 But when we put an invalid discount, the program raises an `AssertionError`. # # A "200% off" discount: # >>> apply_discount(shoes, 2.0) Traceback (most recent call last): File "<input>", line 1, in <module> apply_discount(prod, 2.0) File "<input>", line 4, in apply_discount assert 0 <= price <= product['price'] AssertionError
# # A "-30% off" discount: # >>> apply_discount(shoes, -0.3) Traceback (most recent call last): File "<input>", line 1, in <module> apply_discount(prod, -0.3) File "<input>", line 4, in apply_discount assert 0 <= price <= product['price'] AssertionError |
Another example | Here, we initially set the value of `x` to 1 and use `assert` to test the condition. >>> x = 1 >>> assert x == 1 >>> assert x == 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError >>> |
Tutorials
---------
Ways of supressing exceptions | # Using contextlib >>> import contextlib >>> with contextlib.suppress(FileNotFoundError): ... os.remove('ghostfile') >>> |
Sources
-------
Built-in Exceptions |
No comments:
Post a Comment