3. A Very Short Tutorial

This short tutorial introduces the reader to the basic concepts and features of the Python programming language. It is not a comprehensive introductory tutorial to programming for a complete novice. Instead, it assumes the reader has previous experience programming with an object-oriented programming language.

3.1 Using Python

Python comes bundled with most Unix-based systems, including the Mac OS and various Linux-based distributions. To check for a Python installation, run the python command at the shell. If not installed, then visit the Python language website and follow the instructions on installing Python for your given platform.

The Python Interpreter

You can run the Python interpreter by executing the python command on the shell. In the case, that you have installed Python, but the shell cannot find the command, you should use the full path to the installation or add it to the shell path. Invoking the interpreter launches the Python interactive session with an REPL prompt. The primary prompt, >>>, signals a user to enter statements while the secondary prompt, ..., signals a continuation line, as shown in Listing 2.0.

Listing 2.0: Using the Python prompt
1 >>> def hello():
2 ...     	print("Hello world")
3 ... 
4 >>> 

A user can type in Python statements at the interpreter prompt and get instant feedback. For example, we can evaluate expressions at the REPL and get results as in the listing 2.1.

Listing 2.1: Using the Python prompt
1 >>> var_1 = 3
2 >>> var_2 = 3
3 >>> var_1*var_2
4 9
5 >>> 

We exit the interpreter session by typing Ctrl-D at the prompt.

3.2 Python Statements, Line Structure and Indentation

A program in Python is composed of several logical lines, and the NEWLINE token delimits each of these logical lines. Each logical line is equivalent to a valid statement. Compound statements, however, can be made up of multiple logical lines. A logical line is a combination of one or more physical lines using the explicit or implicit line joining rules. A physical line is a sequence of characters terminated by an end-of-line character. Python implicitly sees physical lines as logical lines eliminating the explicit need for semi-colons at the end of statements as in Java. Semi-colons, however, play a role in Python; they are used to separate multiple logical lines on the same physical line as shown in Listing 2.2.

Listing 2.2: Separating lines using semi-colons
1 >>> i = 5; print i;
2 5

Multiple physical lines can be explicitly joined into a single logical line by use of the Python line continuation character, \, as in Listing 2.3:

Listing 2.3: Joining multiple physical lines
1 >>> name = "Obi Ike-Nwosu"
2 >>> cleaned_name = name.replace("-", " "). \
3 ...                 replace(" ", "")
4 >>> cleaned_name
5 'ObiIkeNwosu'
6 >>> 

Expressions in triple quoted strings, enclosed in parenthesis (…), brackets [….] or braces {…} can span multiple lines; these lines are implicitly joined and thus do not require a line continuation character.

From the discussions above, we can infer that there are two types of statements in Python:

  1. Simple statements that span a single logical line. These include statements such as assignment statements, yield statements etc. Listing 2.4 shows the definition of a simple statement.
Listing 2.4: Definition of a simple Python statement
 1 simple_stmt ::=  expression_stmt
 2                 | assert_stmt
 3                 | assignment_stmt
 4                 | augmented_assignment_stmt
 5                 | pass_stmt
 6                 | del_stmt
 7                 | return_stmt
 8                 | yield_stmt
 9                 | raise_stmt
10                 | break_stmt
11                 | continue_stmt
12                 | import_stmt
13                 | global_stmt
14                 | nonlocal_stmt
15                 
16 ::= means is defined as
17 | means or
  1. Compound statements that span multiple logical lines statements. These include statements such as the while and for. Listing 2.5 is the definition of a compound Python statement.
Listing 2.5: Definition of a compound Python statement
 1 compound_stmt ::=  if_stmt
 2                 | while_stmt
 3                 | for_stmt
 4                 | try_stmt
 5                 | with_stmt
 6                 | funcdef
 7                 | classdef
 8 suite         ::=  stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
 9 statement     ::=  stmt_list NEWLINE | compound_stmt
10 stmt_list     ::=  simple_stmt (";" simple_stmt)* [";"]

Compound statements consist of one or more clauses. A clause consists of a header and a suite. The clause headers for a given compound statement are all at the same indentation level; they begin with a unique identifier, while, if etc., and end with a colon. The header controls the suite execution. Listing 2.6 illustrates this.

Listing 2.6: An example of the if compound statement
1 >>> num = 6
2 # if statement is a compound statement
3     # clause header controls execution of indented block that follows
4 >>> if num % 2 == 0: 
5         # indented suite block
6 ...     print("The number {} is even".format(num))
7 ... 
8 The number 6 is even
9 >>> 

The suite may be a set of one or more statements that follow the header’s colon with each statement separated from the previous by a semi-colon, as shown in Listing 2.7.

Listing 2.7: A compound statement
1 >>> x = 1
2 >>> y = 2
3 >>> z = 3
4 >>> if x < y < z: print(x); print(y); print(z)
5 ... 
6 1
7 2
8 3

The suite is written conventionally as one or more indented statements on subsequent lines that follow the header as in Listing 2.8.

Listing 2.8: A compound statement
 1         >>> x = 1
 2         >>> y = 2
 3         >>> z = 3
 4         >>> if x < y < z: 
 5         ...    print(x)
 6         ...    print(y); 
 7         ...    print(z)
 8         ... 
 9         1
10         2
11         3

Indentations denote code blocks such as function bodies, conditionals, loops and classes. The interpreter computes indentation levels using the leading white-spaces at the start of a logical line; this indentation then determines the grouping of statements. Indentation used within the code body must always match the indentation of the first statement of the block of code.

3.3 Strings

Strings are represented in Python using double "..." or single '...' quotes. Special characters within strings are escaped with \ as shown in Listing 2.9:

Listing 2.9: Escaping characters in a string
1 # the quote is used as an apostrophe, so we escape it for Python to 
2 # treat is as an apostrophe rather than the closing quote for a string
3 >>> name = 'men\'s'
4 >>> name
5 "men's"
6 >>> 

To avoid interpreting characters as special characters, r, should be used before the opening quote of the string, as shown in Listing 2.10.

Listing 2.10: Escaping characters in a string
1 >>> print('C:\some\name')  # here \n means newline!
2 C:\some
3 ame
4 >>> print(r'C:\some\name')  # note the r before the quote
5 C:\some\name

Triple quotes create string literals that span multiple lines, but newlines are automatically added at the end of each line, as shown in Listing 2.11.

Listing 2.11: String literals
 1 >>> para = """hello world I am putting together a 
 2 ... book for beginners to get to the next level in python"""
 3 # notice the new line character 
 4 >>> para
 5 'hello world I am putting together a \nbook for beginners to get to the next level in python'
 6 # printing this will cause the string to go on multiple lines
 7 >>> print(para)
 8 hello world I am putting together a 
 9 book for beginners to get to the next level in Python
10 >>> 

Listing 2.12 shows that we can avoid including the newline character in a string literal by using the line continuation character.

Listing 2.12: Escaping characters in string literals
1 >>> para = """hello world I am putting together a \
2 ... book for beginners to get to the next level in python"""
3 >>> para
4 'hello world I am putting together a book for beginners to get to the next level in python'
5 >>> print(para)
6 hello world I am putting together a book for beginners to get to the next level in Python
7 >>> 

Strings are immutable and cannot be modified once created. There is no character type, so characters are assumed to be strings of unit length. Strings are sequence types so support sequence type operations except for assignment due to their immutability. Strings can be indexed with integers, as shown in Listing 2.13:

Listing 2.13: Indexing strings
1     >>> name = 'obiesie'
2     >>> name[1]
3     'b'
4     >>> 

Listing 2.14 shows how to create a new string by concatenating strings using the add operator, +.

Listing 2.14: Concatenating strings
1 >>> name = 'obiesie'
2 >>> surname = " Ike-Nwosu"
3 >>> full_name = name + surname
4 >>> full_name
5 'obiesie Ike-Nwosu'
6 >>> 

One can also concatenate two or more string literals by writing them next to each other as shown in Listing 2.15:

Listing 2.15: Concatenating strings
1 >>> 'Py' 'thon'
2 'Python'
3 >>> 

The built-in method len computes the length of a string, as shown in Listing 2.16.

Listing 2.16: Finding length of strings
1 >>> name = "obi"
2 >>> len(name)
3 3
4 >>> 

3.4 Flow Control

if-else and if-elif-else statements

Python supports the if statement for conditional execution of a code block as in Listing 2.17.

Listing 2.17: Using the if statement
1 >>> name = "obi"
2 >>> if name == "obi":
3 ...     print("Hello Obi")
4 ... 
5 Hello Obi
6 >>> 

Zero or more elif statements and an optional else statement come after the if statement. The else statement executes when none of the conditions in the if, or elif statements is True as shown in Listing 2.18.

Listing 2.18: Using the else clause
1 >>> if name == "obi":
2 ...     	print("Hello Obi")
3 ... elif name == "chuks":
4 ... 	print("Hello chuks")
5 ... else:
6 ...	print("Hello Stranger")
7 Hello Stranger
8 >>> 
for and range statements

The while and for statements are the primary looping constructs provided by Python.

The for statement in Python iterates over sequence types (lists, sets, tuples, etc.). More generally, the for loop can iterate over any object that implements the Python iterator protocol. This protocol is discussed further in later chapters. Listing 2.19 shows how the use of the for loop.

Listing 2.19: the for loop
1 >>> names = ["Joe", "Obi", "Chris", "Nkem"]
2 >>> for name in names:
3 ...     print(name)
4 ... 
5 Joe
6 Obi
7 Chris
8 Nkem
9 >>> 

Most programming languages have a syntax similar to that shown in Listing 2.20 for iterating over a progression of numbers:

Listing 2.20: C and Java style for loop
1 for(int x = 10; x < 20; x = x+1) {
2             // do something here
3         }

Python replaces the loop in Listing 2.20 with the simpler range() statement that generates an arithmetic progression of integers. Listing 2.21 shows the usage of the range statement.

Listing 2.21: Python for loop
 1 >>> for i in range(10, 20):
 2 ...     print i
 3 ... 
 4 10
 5 11
 6 12
 7 13
 8 14
 9 15
10 16
11 17
12 18
13 19
14 >>> 

The range statement has a syntax of range(start, stop, step). The stop value is never part of the progression.

while statement

The while statement executes the statements in its suite as long as the condition expression in the while statement evaluates to True. Listing 2.22 shows the while loop generating an arithmetic progression.

Listing 2.22: Using the while loop
 1 >>> counter = 10
 2 >>> while counter > 0: # the conditional expression is 'counter>0'
 3 ...     print(counter)
 4 ...     counter = counter - 1
 5 ... 
 6 10
 7 9
 8 8
 9 7
10 6
11 5
12 4
13 3
14 2
15 1
break and continue statements

Whenever an executing encounters the break statement in a loop, the execution flow exits the loop as in Listing 2.23.

Listing 2.23: the break statement
 1 >>> for i in range(10):
 2 ...     if i == 5:
 3 ...             break
 4 ...     else:
 5 ...             print(i)
 6 ... 
 7 0
 8 1
 9 2
10 3
11 4

The continue keyword forces the start of the next iteration of a loop ignoring any subsequent statement as in Listing 2.24.

Listing 2.24: The continue statement
 1 >>> for i in range(10):
 2         # if i is 5 then continue so print statement is ignored and the next iteration
 3         # continues with i set to 6
 4 ...     if i == 5:
 5 ...             continue
 6 ...     print("The value is " + str(i))
 7 ... 
 8 The value is 0
 9 The value is 1
10 The value is 2
11 The value is 3
12 The value is 4
13 # no printed value for i == 6
14 The value is 6
15 The value is 7
16 The value is 8
17 The value is 9

In Listing 2.24, the flow of execution skips printing the number 5 because of the continue statement.

else clause with looping constructs

Python has support for using an else keyword in conjunction with a loop statement. The statements within the suite of an else statement that follow loop constructs get executed if a break statement did not terminate the loop. Listing 2.25 demonstrates this feature.

Listing 2.25: For-else clause
 1 # loop exits normally 
 2 >>> for i in range(10):
 3 ...     print(i)
 4 ... else:
 5 ...     print("I am in quirky else loop")
 6 ... 
 7 0
 8 1
 9 2
10 3
11 4
12 5
13 6
14 7
15 8
16 9
17 I am in quirky else loop
18 >>> 

If a break statement caused the loop to exit, the suite of the else statement is skipped, as in Listing 2.26.

Listing 2.26: For-else clause
 1 >>> for i in range(10):
 2 ...     if i == 5:
 3 ...             break
 4 ...     print(i)
 5 ... else:
 6 ...     print("I am in quirky else loop")
 7 ... 
 8 0
 9 1
10 2
11 3
12 4
13 >>> 
Enumerate

Sometimes, when iterating over a list, a tuple or a sequence in general, it is necessary to have access to the items and corresponding indices of the items in the sequence. A while statement can achieve for this, as in Listing 2.27:

Listing 2.27: Python lists
 1 >>> names = ["Joe", "Obi", "Chris", "Jamie"]
 2 >>> name_count = len(names)
 3 >>> index = 0
 4 >>> while index < name_count:
 5 ...     print("{}. {}".format(index, names[index]))
 6 ...     index = index + 1
 7 ... 
 8 0. Joe
 9 1. Obi
10 2. Chris
11 3. Jamie

The solution in Listing 2.27 is how one would go about it in most languages, but Python has a better alternative in the form of the enumerate keyword. The solution in Listing 2.27 can be reworked beautifully in Python, as shown in Listing 2.28.

Listing 2.28: Enumerating over a list
1 >>> for index, name in enumerate(names):
2 ...     print("{}. {}".format(index, name))
3 ... 
4 0. Joe
5 1. Obi
6 2. Chris
7 3. Jamie
8 >>> 

3.5 Functions

Named functions are defined with the def keyword, which must be followed by the function name and the parenthesized list of formal parameters. The return keyword returns a value from a function definition. Listing 2.29 is an example of a function definition.

Listing 2.29: Defining a function
1 def full_name(first_name, last_name):
2     return " ".join((first_name, last_name))

We execute functions by calling the function name with required arguments in parenthesis, for example full_name("Obi", "Ike-Nwosu"). Python functions can return multiple values by returning a tuple of the required values as shown in Listing 2.30, where we return the quotient and remainder from a division operation:

Listing 2.30: Calling a function
1 >>> def divide(a, b):
2 ...     return divmod(q, r)
3 ... 
4 >>> divide(7, 2)
5 (3, 1)
6 >>> 

Functions do not require a return keyword. In that case, the default returned value is None as in Listing 2.31.

Listing 2.31: Function without return keyword
 1 >>> def print_name(first_name, last_name):
 2 ...     print(" ".join((first_name, last_name)))
 3 ... 
 4 >>> print_name("Obi", "Ike-Nwosu")
 5 Obi Ike-Nwosu
 6 >>> x = print_name("Obi", "Ike-Nwosu")
 7 Obi Ike-Nwosu
 8 >>> x
 9 >>> type(x)
10 <type 'NoneType'>
11 >>> 

The return keyword does not even have to return a value in Python as in Listing 2.32.

Listing 2.32: Using return without a value
1 >>> def dont_return_value():
2 ...     print("How to use return keyword without a value")
3 ...     return
4 ... 
5 >>> dont_return_value()
6 How to use return keyword without a value

Python also supports anonymous functions defined with the lambda keyword. Python’s lambda support is somewhat limited, crippled a few people may say because it supports only a single expression in the body of the lambda expression. Lambda expressions are another form of syntactic sugar and are equivalent to the conventional named function definition. Listing 2.33 is an example of a lambda function definition.

Listing 2.33: Defining lambda functions
1 >>> square_of_number = lambda x: x**2
2 >>> square_of_number
3 <function <lambda> at 0x101a07158>
4 >>> square_of_number(2)
5 4
6 >>> 

3.6 Data Structures

Python has several built-in data structures that make programming easier. The built-in data structures include lists, tuples and dictionaries.

  1. Lists: Lists are a mutable sequence of objects built using square brackets, [] or the list() function around the objects. The empty list is denoted by []. Lists preserve the order of items after creation and as they are insert into the list. Lists are sequence types so support integer indexing and all other sequence type sub-scripting that we will see in subsequent chapters. Lists are indexed by integers starting with zero and going up to the length of the list minus one as in Listing 2.34.
Listing 2.34: List data structure
1 >>> name = ["obi", "ike", "nwosu"]
2 >>> name[0]
3 'obi'
4 >>> 

Elements are added to the end of a list by appending to it as in Listing 2.35.

Listing 2.35: Manipulating a list
1 >>>     name = ["obi", "ike", "nwosu"]
2 >>> name.append("nkem")
3 >>> names 
4 ["obi", "ike", "nwosu", "nkem"]

Elements can also be added to other parts of a list not just the end by using the insert method.

Listing 2.36: Maninpulating a list
1 >>> name = ["obi", "ike", "nwosu"]
2 >>> name.insert(1, "nkem")
3 >>> names 
4 ["obi", "nkem", "ike", "nwosu"]

We can concatenate two or more lists with the + operator as in Listing 2.37.

Listing 2.37: Manipulating a list
1 >>> name = ["obi", "ike", "nwosu"]
2 >>> name1 = ["James"]
3 >>> name + name1 
4 ["obi", "ike", "nwosu", "James"]

To get a full list of all methods of the list type, run the help(list) command.

  1. Tuples: These are also another type of sequence structures. A tuple consists of several comma-separated objects such as in Listing 2.38.
Listing 2.38: Tuple datastructure
1 >>> companies = "Google", "Microsoft", "Tesla"
2 >>> companies
3 ('Google', 'Microsoft', 'Tesla')
4 >>> 

When defining a non-empty tuple, the parenthesis is optional, but when the tuple is part of a larger expression, the parenthesis is required. The parenthesis come in handy when defining an empty tuple as in Listing 2.39.

Listing 2.39: Tuple manipulation
1 >>> companies = ()
2 >>> type(companies)
3 <class 'tuple'>
4 >>> 

Tuples have a quirky syntax that some people may find surprising. When defining a single element tuple, we must include a comma after the single element regardless of the presence of a pair of parenthesis. If the comma is left out, then the result of the expression is not a tuple. For instance:

Listing 2.40: Tuple manipulation
 1 >>> company = "Google",
 2 >>> type(company)
 3 <class 'tuple'>
 4 >>> 
 5 >>> company = ("Google",)
 6 >>> type(company)
 7 <class 'tuple'>
 8 # absence of the comma returns the value contained within the parenthesis
 9 >>> company = ("Google")
10 >>> company
11 'Google'
12 >>> type(company)
13 <class 'str'>
14 >>> 

Tuples are integer indexed just like lists but are immutable; once created the contents cannot be changed by any means such as by assignment; Listing 2.41 shows this.

Listing 2.41: Tuple manipulation
1 >>> companies = ("Google", "Microsoft", "Palantir")
2 >>> companies[0]
3 'Google'
4 >>> companies[0] = "Boeing"
5 Traceback (most recent call last):
6     File "<stdin>", line 1, in <module>
7 TypeError: 'tuple' object does not support item assignment
8 >>> 

Note that, if an object in a tuple is mutable, then we can modify the content of such an object as in Listing 2.42:

Listing 2.42: Tuple manipulation
1 >>> companies = (["lockheedMartin", "Boeing"], ["Google", "Microsoft"])
2 >>> companies
3 (['lockheedMartin', 'Boeing'], ['Google', 'Microsoft'])
4 >>> companies[0].append("SpaceX")
5 >>> companies
6 (['lockheedMartin', 'Boeing', 'SpaceX'], ['Google', 'Microsoft'])
7 >>> 
  1. Sets: A set is an unordered collection of objects that do not contain any duplicates. An empty set is created using set() or by using curly braces, {}. Sets are unordered so unlike tuples or lists we cannot index sets. However sets, except for frozen sets, are mutable so one can add, update or remove from a set as in Listing 2.43:
Listing 2.43: The set data structure
 1 >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
 2 >>> basket_set = set()
 3 >>> basket_set
 4 set()
 5 >>> basket_set.update(basket)
 6 >>> basket_set
 7 {'pear', 'orange', 'apple', 'banana'}
 8 >>> basket_set.add("clementine")
 9 >>> basket_set
10 {'pear', 'orange', 'apple', 'banana', 'clementine'}
11 >>> basket_set.remove("apple")
12 >>> basket_set
13 {'pear', 'orange', 'banana', 'clementine'}
14 >>> 
  1. Dictionary: This is a mapping data structure commonly referred to as an associative array or a hash table in other languages. Dictionaries are also commonly called dicts and client code can index them by keys although the keys must be immutable. A pair of braces, {...} or method dict() is used to create a dict. Dictionaries are an unordered set of key: value pairs, in which the keys are unique. We initialize a dictionary by placing a set of key: value pairs within the braces, as shown in Listing 2.44.
Listing 2.44: The dict data structure
1 ages = {"obi": 24,
2         "nkem": 23,
3         "Chris": 23
4         }

The main operation offered by dictionaries is the storage and retrieval of values by the key as in Listing 2.45.

Listing 2.45: Dict manipulation
1 >>> ages["obi"]
2 24

Dictionaries are mutable, so the values indexed by a key are mutable, keys can also be deleted and added to the dict.

Python’s data structures are not limited to just those listed in this section. For example, the collections module provides additional data structures such as queues and deques however, the data structures listed in this section form the workhorse for most Python applications. Use the help() function with data structure class name as an argument to get a better insight into the capabilities of a data structure.

3.7 Classes

The class statement defines new types in Python as in Listing 2.46:

Listing 2.46: Defining classes
 1 class Account:
 2     # class variable that is common to all instances of a class
 3     num_accounts = 0
 4 
 5     def __init__(self, name, balance):
 6         # start of instance variable
 7         self.name = name 
 8         self.balance = balance 
 9         # end of instance variables
10         Account.num_accounts += 1
11         
12     def deposit(self, amt):
13         self.balance = self.balance + amt 
14 
15     def withdraw(self, amt):
16         self.balance = self.balance - amt 
17 
18     def inquiry(self):
19         return "Name={}, balance={}".format(self.name, self.balance) 
20 
21     @classmethod 
22     def from_dict(cls, params):
23         params_dict = json.loads(params)
24         return cls(params_dict.get("name"), params_dict.get("balance"))

Classes in Python just like classes in other languages have class variables, instance variables, class methods, static methods and instance methods. When defining classes, the base classes go in the parenthesis that follows the class name. For those that are familiar with Java, the __init__ method is something similar to a constructor; it is in this method that we initialize instance variables. The class definition in Listing 2.46 is initialized by calling the defined class with required arguments to __init__ in parenthesis ignoring the self argument as in Listing 2.47.

Listing 2.47: Instantiating a class
1 >>> acct = Account("obie", 10000000)

Methods in a class that are defined with self as the first argument are instance methods. The self argument is similar to this in Java and refers to the object instance. Methods are called in Python using the dot notation syntax, as in Listing 2.48:

Listing 2.48: Manipulating class instances
1 >>> acct = Account("obie", 10000000)
2 >>>account.inquiry()
3 Name=obie, balance=10000000

We can view all attributes of an object by calling the dir function with the object as an argument.

3.8 Modules

Functions and classes provide the means for structuring Python code, but as the code grows in size and complexity, we need to be able to split such code into multiple files with each source file containing related definitions. The source files can then be imported as necessary to access definitions in any of such source file. In Python, we refer to source files as modules and modules have the .py extensions.

For example, if the Account class definition from the previous section is saved to a module called Account.py, we can use this module elsewhere, by importing it as shown in Listing 2.49.

Listing 2.49: Importing a module
1 >>> import Account
2 >>> acct = Account.Account("obie", 10000000)

Note that the import statement takes the name of the module without the .py extension. Using the import statement creates a namespace, in this case, the Account namespace and all definitions in the module are available in such namespace. We can create an alias for an imported module using the as keyword, so the example from Listing 2.49 can be reworked as shown in Listing 2.50.

Listing 2.50: Importing a module using as
1 >>> import Account as acct
2 >>> account = acct.Account("obie". 10000000)

It is also possible to import only the needed definitions from the module resulting in the snippet in Listing 2.51:

Listing 2.51: Importing definitions from a module
1 >>> from Account import Account
2 >>> account = Account("obie", 10000000)

One can import all the definitions in a module by using the wild card import, as shown in Listing 2.52:

Listing 2.52: Import using the wild card
1 >>> from Account import *

This method of imports is problematic sometimes as it can result in name clashes when one of the name definitions this is imported already exists in the current namespace. Modules can be further grouped into packages and so on. Modules are also objects in Python so we can introspect on them using the dir introspection function. We discuss modules and packages in-depth in subsequent chapters.

3.9 Exceptions

Python has support for exceptions and exception handling. For example, dividing by zero raises a ZeroDivisionError error as in Listing 2.53.

Listing 2.53: Example of a runtime exception
1 >>> 2/0
2 Traceback (most recent call last):
3   File "<stdin>", line 1, in <module>
4 ZeroDivisionError: integer division or modulo by zero
5 >>> 

The try...catch statement is for handling exceptions in Python. For example, Listing 2.54 shows how we can handle the divide by zero exception from Listing 2.53 without the program crashing.

Listing 2.54: Exception handling
1 >>> try:
2 ...     2/0
3 ... except ZeroDivisionError as e:
4 ...     print("Attempting to divide by 0. Not allowed")
5 ... 
6 Attempting to divide by 0. Not allowed
7 >>> 

Exceptions in Python are of different types. For example, if an attempt were made to catch an IOError in the previous snippet, the program would terminate because the resulting exception type is a ZeroDivisionError exception. To catch all types of exceptions with a single handler, try...catch Exception is used but this is advised against as it becomes impossible to tell what kind of exception has occurred thus masking the exception.

We can also define custom exceptions to handle exceptional situations in our code. To do this, we define a custom exception class that inherits from the Exception base class.

3.10 Input and Output

Python, as expected, has support for reading and writing to and from input and output sources. The file on the hard drive is the most popular IO device. The content of a file can be opened and read from using the snippet in Listing 2.55:

Listing 2.55: File IO
1 f = open("afile.txt")
2 line = f.readline()
3 while line:
4     print(line)
5     line = f.readline()

The open method returns a file object or throws an exception if the file does not exist. The file object supports several methods such as read that reads the entire content of the file into a string or readline that reads the contents of the file one line at a time. Listing 2.56 shows some syntactic sugar provided by Python for iterating over lines in a file.

Listing 2.56: Reading from a file
1 for line in open("afile.txt"):
2     print(line)

Python also supports writing to a file, as shown in Listing 2.57:

Listing 2.57: Writing to a file
1 f = open("out.txt", "w")
2 contents = ["I", "love", "python"]
3 for content in contents:
4     f.write(content)
5 f.close()

Python also has support for writing to standard input and standard output. This is done using the sys.stdout.write() or the sys.stdin.readline() from the sys module.

3.11 Getting Help

The Python programming language comes with detailed documentation that is available at the interpreter prompt by using the help method. To get more information about a syntactic construct or data structure, pass it as an argument to the help function, for example help(list).