May 10, 2021 Python3
As a beginner of Python, when you first learn Python programming, you often see false information, which we didn't mention earlier, and we'll cover this section specifically.
Python has two types of errors that are easy to identify: syntax errors and exceptions.
Python's syntax errors, or parsing errors, are often encountered by beginners, as shown in the following examples
>>> while True print('Hello world')
File "<stdin>", line 1, in ?
while True print('Hello world')
^
SyntaxError: invalid syntax
In this example, the function print() is checked for an error, which is that it is preceded by a colon (:).
The syntax analyzer points out the wrong line and marks a small arrow where the error was first found.
Even if the Syntax of the Python program is correct, an error can occur when you run it. Errors detected during the run are called exceptions.
Most exceptions are not handled by the program and are presented here in the form of error messages:
>>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in ? ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in ? NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: Can't convert 'int' object to str implicitly
Exceptions appear in different types, all printed out as part of the information: the types in the example are ZeroDivisionError, NameError, and TypeError.
The previous section of the error message shows the context in which the exception occurred and displays specific information as a call stack.
In the following example, the user is asked to enter a legitimate integer, but the user is allowed to interrupt the program (using control-C or the method provided by the operating system). T he user-interrupted information throws a KeyboardInterrupt exception.
>>> while True:
try:
x = int(input("Please enter a number: "))
break
except ValueError:
print("Oops! That was no valid number. Try again ")
Try statements work as follows;
A try statement may contain multiple except clauses that handle different specific exceptions. At most one branch is executed.
The handler will handle exceptions only in the corresponding try clause, not in other try handlers.
An except clause can handle multiple exceptions at the same time, which are placed in parentheses and become a metagroup, for example:
>>> except (RuntimeError, TypeError, NameError):
pass
The last except clause ignores the name of the exception, which is used as a wildcard. You can use this method to print an error message and then throw the exception again.
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
The try except statement also has an optional else clause, which, if used, must be placed after all the except clauses. T his clause will be executed when no exceptions occur in the try clause. F or example:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except IOError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
Using the else clause is better than putting all the statements in the try clause, which avoids unexpected exceptions that excelt and don't catch.
Exception handling does not only deal with exceptions that occur directly in the try clause, but also in functions called in the clause (and even those called indirectly). For example:
>>> def this_fails():
x = 1/0
>>> try:
this_fails()
except ZeroDivisionError as err:
print('Handling run-time error:', err)
Handling run-time error: int division or modulo by zero
Python uses the raise statement to throw a specified exception. For example:
>>> raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: HiThere
The only argument for raise specifies the exception to be thrown. It must be an instance of an exception or a class of an exception (that is, a sub-class of Exception).
If you just want to know if this throws an exception and don't want to handle it, a simple raise statement can throw it again.
>>> 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 ?
NameError: HiThere
You can have your own exception by creating a new exception class. Exceptions should be inherited from the Exception class, either directly or indirectly, for example:
>>> class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
>>> try:
raise MyError(2*2)
except MyError as e:
print('My exception occurred, value:', e.value)
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'
In this example, class Exception defaults to __init__() overwritten.
Exception classes can do anything like any other class, but are generally simpler, provide only error-related properties, and allow code that handles exceptions to easily obtain this information.
When creating a module has the potential to throw many different exceptions, it is common practice to create a base exception class for the package, and then create different sub-classes for different error scenarios based on this base class:
class Error(Exception):
"""Base class for exceptions in this module."""
pass
class InputError(Error):
"""Exception raised for errors in the input.
Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""
def __init__(self, expression, message):
self.expression = expression
self.message = message
class TransitionError(Error):
"""Raised when an operation attempts a state transition that's not
allowed.
Attributes:
previous -- state at beginning of transition
next -- attempted new state
message -- explanation of why the specific transition is not allowed
"""
def __init__(self, previous, next, message):
self.previous = previous
self.next = next
self.message = message
Most exceptions end with "Error", just like standard exceptions.
The try statement also has an optional clause that defines the cleanup behavior that is performed in any case. For example:
>>> try:
raise KeyboardInterrupt
finally:
print('Goodbye, world!')
Goodbye, world!
KeyboardInterrupt
The above example executes the final clause regardless of whether there is an exception in the try clause.
If an exception is thrown in the try clause (or in the except and else clauses) without any except intercepting it, the exception is thrown again after the final clause is executed.
Here's a more complex example (with the except and final clauses in the same try statement):
>>> 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 ?
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
Some objects define a standard cleanup behavior, regardless of whether the system has successfully used it, and once it is not needed, the standard cleanup behavior is performed.
This example shows an attempt to open a file and then print the content to the screen:
for line in open("myfile.txt"):
print(line, end="")
The problem with this code is that when executed, the file remains open and is not closed.
The keyword with statement guarantees that an object such as a file will perform its cleanup correctly after it is used:
with open("myfile.txt") as f:
for line in f:
print(line, end="")
After the above code is executed, file f will always close even if something doesn't go wrong during processing.