An Introduction to Python Part 2

An Introduction to Python Part 2

By Jeff Tranter

Continuing our three-part series on the Python programming language, in this post we'll look at more of the core language features.

File I/O

Python provides a number of functions for file input and output, similar to what is provided in standard libraries for languages like C and C++. Shown below are examples of some of the more commonly used functions to give you a flavor for what is offered. Most of these should be self-explanatory. The last function may not be as obvious -- it prompts the user and reads from standard input.

f = open("filename", "w", encoding="utf8")
f.write("foo")
f.writelines(lines)
f.read(n)
f.readline()
f.readlines(n)
f.flush()
f.tell()
f.close()
f.seek()
f.truncate()
s = input("prompt> ")

Built-in Functions

The Python language has a group of built-in, commonly used functions that are always available. They are listed in the table below. You can consult the Python documentation for details.

all() any() ascii() bin() bool()
bytearray() bytes() callable() chr() classmethod()
compile() complex() delattr() dict() dir()
divmod() enumerate() eval() exec() filter()
float() format() frozenset() getattr() globals()
hasattr() hash() help() hex() id()
input() int() isinstance() issubclass() iter()
len() list() locals() map() max()
memoryview() min() next() object() oct()
open() ord() pow() print() property()
range() repr() reversed() round() set()
setattr() slice() sorted() staticmethod() str()
sum() super() tuple() type() vars()
zip() __import__()      

Collection Types

Python provides support for container classes, known as collections. These tend to be used heavily and are higher level in nature than the basic containers, such as arrays offered in languages like C.

In Python, tuples are an ordered sequence of zero or more objects. They can contain objects of different types. Functions are provided to extract items. Tuples are immutable, meaning that they can't be changed. Below are some examples showing the syntax for tuples, which use parentheses to separate items. The index operator (square brackets) can access elements given a numeric index.

t = ()
t = ("one")
t = ("one", "two")
t = ("one", "two", 3.14)
>>> t
('one', 'two', 3.14)
>>> t[0]
'one'
>>> t[2]
3.14

The next collection type, lists, are similar to tuples but are mutable. You can insert and delete elements of the list. The syntax uses square brackets to separate items. Some examples from an interactive session are shown below.

l = ["one", "two", 3.14]
l.insert(1, "new")
>>> l
['one', 'new', 'two', 3.14]
>>> del l[1]
>>> l
['one', 'two', 3.14]

Dictionaries are associative arrays with key/value pairs. The keys are unique and can be of any type. Dictionaries are unordered and mutable. A dictionary literal uses curly brackets with the keys and values separated by colons. Below are some examples to give you a feel for how they work.

# e.g. names and ages, key is name, value is age.
d = {"Fred": 29, "Bob": 32, "Ed": 45}
>>> d
{'Ed': 45, 'Bob': 32, 'Fred': 29}
>>> d["Bob"]
32
>>> d.keys()
dict_keys(['Ed', 'Bob', 'Fred'])

Sets are unordered collections of which there are two types: set and frozenset. Set is mutable, while frozenset is not. Every item in a set must be unique. Python provides the standard set operators for union, intersection and difference. You typically create a set from a sequence, as in the example below.

>>> s = set(("one", "two", "three"))
>>> s
{'one', 'three', 'two'}
>>> "one" in s
True

Note that the order of the items displayed above changed from the order in which it was defined because sets are unordered. The order they are listed is arbitrary because sets are considered to be equivalent if they contain the same elements.

You can nest and combine any of these collection types. For example, you can have lists of lists, dictionaries of lists, and so on.

Control Structures

Another key feature of any programming language is to provide control structures for branching and looping. The general form for branching in Python is:

if expression:
  suite1
elif expression:
  suite2
else:
  suite3

Suite is the Python term for any block of code. Block structure is signified by indentation. (The usual convention is four spaces per level.) This scheme avoids the dangling else trap typical of some other languages.

Here is an example illustrating branching:

if x == 1:
    print(“one”)
elif x == 2:
    print(“two”)
else:
    print(“many”)

C and C++ programmers learning Python often forget the colon after the expression. They may also tend to put extraneous (but harmless) semicolons at the end of lines. Later, I’ll mention a tool that can find issues like this.

Note that unlike with C and C++, in Python assignment cannot occur inside expressions. This avoids the problems encountered in C programs where typing = in an expression when == was intended (although modern compilers tend to warn about this).

Python also has no equivalent to the switch statement. It uses multiple elifs instead.

Control structures for looping include the while statement, which uses the format:

while expression:
    suite

Here is an example:

i = 1
while i <= 10:
    print(i)
    i += 1

While supports a break statement to get out of loop. You can have an else: clause at the end, but this feature is rarely used.

The ubiquitous for loop takes this form in Python:

for var in iterable:
    suite1
else:
    suite2

Here is an example of iterating over the characters in a string:

for c in "abcdefg":
  print(c)

An else clause, if specified, is executed when the for condition is false. This is also a rarely used feature.

Here are a few more examples of for loops:

# Prints 1 to 9
for i in range(1, 10):
  print(i)

l = ["one", "two", "three"]
for i in l:
  print(i)

d = {"Fred": 29, "Bob": 32, "Ed": 45}
for k in d.keys():
  print("key =", k, "value =", d[k])

The range() function is a common way to iterate over a range of values.

Functions

Python supports defining functions that can accept arguments and return a value. The general form is:

def functionName(optional_parameters):
  suite

Here is a concrete example:

def hello():
    print("Hello!")

>>> hello()
Hello!

In part one of this series we showed this simple factorial function:

def factorial(n):
    if n > 1:
        return n * factorial(n-1)
    else:
        return 1

Here is a version that uses a loop, which is generally more efficient than the recursive version:

def factorial1(n):
    fact = 1
    for i in range(1, n+1):
        fact *= i
    return fact

>>> factorial(10)
3628800

Python allows functions to have default values for arguments, as in this simple example. If an argument is omitted in a function call it uses the default value:

def hello(msg="Hello!"):
    print(msg)

>>> hello()
Hello!
>>> hello("Hi")
Hi

You can also specify arguments by name when calling them. Some examples are below:

def greeting(name, age):
  print("My name is", name, "and I am", age, "years old.")

>>> greeting("Jeff", 29)
My name is Jeff and I am 29 years old.
>>> greeting(name="Jeff", age=29)
My name is Jeff and I am 29 years old.
>>> greeting(age=29, name="Jeff")
My name is Jeff and I am 29 years old.

Keyword arguments can make source code more readable and less error-prone.

Though we can’t cover all of the more advanced aspects of Python functions you may want to read about them in the documentation. They include generator functions, lambda functions, dynamic function creation, partial function application, and functions that are local to a scope.

Exception Handling

Most modern programming languages support error trapping and handling using exceptions. In Python the general format for exceptions is:

try:
  body
  raise Exception()
except Exception as e:
  exception handling
finally:
  cleanup

Only the try and except sections are required. Exceptions tend to be used quite regularly in Python to simplify error handling.

Going back to our factorial function example, we could add some simple error checking for an invalid command line argument by including a few more lines of code (shown below in bold):

import sys
def factorial(n):
    if n > 1:
        return n * factorial(n-1)
    else:
        return 1
try:
    print(factorial(int(sys.argv[1])))
except:
    print("Invalid argument.")

In production code we would probably want to add error checking in the factorial function itself, as well as in the main function, and provide the user with a more meaningful indication of the error (e.g. whether the argument was out of range or simply missing).

Summary

To date we've covered a lot of Python’s core features. In the final installment we'll look at modules and explore how object-oriented programming is supported. This is used by the Qt bindings. I’ll show two different Python bindings for Qt, as well as offer some programming tips and references so you can learn more about Python.