×
Namespaces

Variants
Actions

Archived:Python on Symbian/02. Basic Python Elements

From Nokia Developer Wiki
Jump to: navigation, search

Archived.pngAquivado: Este artigo foi arquivado, pois o conteúdo não é mais considerado relevante para se criar soluções comerciais atuais. Se você achar que este artigo ainda é importante, inclua o template {{ForArchiveReview|escreva a sua justificativa}}.

All PySymbian articles have been archived. PySymbian is no longer maintained by Nokia and is not guaranteed to work on more recent Symbian devices. It is not possible to submit apps to Nokia Store.

Article Metadata
Article
Created: hamishwillee (29 Nov 2010)
Last edited: hamishwillee (08 May 2013)

Original Author: Mike Jipping

This chapter introduces the basics of the Python language. When this wiki books is complete, the first chapter will describe how to set up the Python environment for your PC and your Symbian device. In this chapter, we begin to write code that runs in both environments.

This chapter is not meant to be a definitive guide to Python. Instead, we provide an overview of the basic elements of the language and some simple examples to help you start programming. Where useful, we include comparative examples to explain how Python behavior differs from that of C++ and Java.

This chapter is applicable to all versions of Python. We cover the basics of the Python language; specifics of the Symbian implementation of Python are covered in later chapters.

Contents

Introduction

Python was designed as a reaction to, and an improvement on, compiled languages such as C++ and Java, and scripting languages such as Perl. It has a mixture of features from several languages — even functional languages — and was designed to put the best features of each into a language that could support the type of rapid development that scripting languages are able to support. Its design philosophy focused on code readability.

This means that Python supports multiple programming styles and paradigms. It is primarily an imperative language and programs can be written entirely from an imperative perspective. Python supports object-orientation at its core and supports structured programming. However, there is also support for functional programming (Lisp programmers will be comfortable with its use of lambda calculus) and you can even find aspect programming in Python's design (for example, meta-programming).

Python combines the best parts of programming languages into a highly readable language that executes in a scripting environment. By 'scripting', we mean that Python programs are executed directly by an execution engine without being translated into some native machine code. This execution engine is often referred to as an 'interpreter'; it interprets Python statements by reading and parsing them, and executes that interpretation by running machine code on the host computer's hardware. When compared to languages like C++ that compile to machine language the Python interpreter represents an extra layer of execution, which can be a program performance issue. Interpreted Python programs tend to run slower than similar programs written in compiled languages. However, much work has been done on Python interpreters to minimize this performance issue.

The scripting nature of Python has many benefits. Interpreters can take many forms, which means Python can be used in many places. It can become part of other software systems, such as Web browsers or system controllers, and can be ported to different architectural platforms with ease. Python supports experimentation with language features because its environment can be extended with new plugin programs.

All of Python's many design aspects will become more apparent as we introduce Python programming.

Variables

A strength of Python is the ability to abstract that concept of computer memory into something that is easy to use: the idea of variables. In Python, variables hold objects and objects represent data. Variables have identifiers, or names. Names must begin with a letter, but can have any combination of letters, numbers, and the "_" character. Python uses assignment to give a variable a value and, references the value of the variable when the variable is used by name.

Python does not require variables to be declared and provides no mechanism to do so — simply assigning a value to a variable makes it exist (this is a significant difference between Python and more structured, compiled languages like C++ or Java). Thus, we can assign values like this:

>>> hours = 42.5
>>> hourly_wage = 12
>>> payment = hours * hourly_wage
>>> payment
510.0

Using a variable without first assigning it a value, however, is an error. Consider the attempt below to use overtime without assigning a value:

>>> payment = hours * hourly_wage + overtime * hourly_wage * 1.5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'overtime' is not defined

Basic Types

Another strength of Python is its ability to view data in many different ways — as numbers, strings of characters, or pixels in an image. As with other programming languages, variables in Python are typed, that is, they are classified by the values they can take and the operations that can be performed on them. Types that are so fundamental to Python programming that they are built into the language are referred to as core types. Python has 11 built-in types. These are summarized in Table 2-1.

Table 2.1: Python Built-in Types
Object Type Description Examples
int A whole number with fixed precision of unlimited magnitude 19, 72, -12
float A floating point number with a fractional part of system-defined precision 3.14159, -50.309, 100.2
complex A number with real and imaginary parts 7+4.5j
boolean A truth value True, False
string A sequence of Unicode characters 'mobile', "Python's"
byte A sequence of 8-bit bytes b'one', b"characters"
list A sequence of objects, which can be of mixed types [7,"lowest",False]
tuple A sequence of objects, which can be of mixed types (7,"lowest",False)
dictionary A group of key/value pairs, which can be of mixed types {"key1":17,

"key2":True}

set An unordered sequence of objects, which can be of mixed types, with no duplicates {7,"lowest",False}
file The representation of data kept in storage myFile = open("README",'r')

Several of these types will look familiar: int, float, boolean, string, and even complex are represented in many programming languages. Note that boolean True and False values are capitalized. There are a few types, however, that require more explanation:

  • The byte type is a representation of 8-bit quantities. Characters fit into 8-bit chunks (ASCII characters do; UNICODE characters do not) and Python represents bytes as the character 'b' followed by a string. In Table 2-1, b'one' is a sequence of three bytes: 01101111, 01101110, and 01100101.
  • The list and tuple types can be viewed as collections, like arrays or vectors in other languages. Actually, they are like a combination of structs and arrays from C and C++, and may have any type of data in them. In Table 2-1, we can see an integer, a string and a boolean in each collection. Lists are represented using square brackets; tuples are represented using parentheses.
The difference between lists and tuples is their immutability. Immutable objects in Python cannot be changed. Tuples (and strings) in Python are immutable. This means you cannot change a tuple once it is set. Lists, on the other hand, can be changed.
For example, we can assign a list of values to a variable, change the list, then print it back out:
>>> thelist = ['trees', 'birds', 'flowers']
>>> thelist
['trees', 'birds', 'flowers']
>>> thelist[1] = 'cows'
>>> thelist
['trees', 'cows', 'flowers']
Here, we were able to change items in the list using array-like notation. If we tried this with a tuple, however, we would get an error:
>>> thetuple = ('trees', 'birds', 'flowers')
>>> thetuple
('trees', 'birds', 'flowers')
>>> thetuple[1] = 'cow'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
The concept of immutability can be used to guarantee that objects remain constant throughout a program. Immutability forces programmers to assign new values to new variables, rather than changing values in place.
  • A dictionary in Python is a mapping — like the hash table type in Java. Items in a dictionary are stored in key/value pairs. Variables that hold dictionary objects can be referenced with the key and return the value. Consider these examples:
>>> thedictionary = {"item": "coffee", "onshelf": True, "quantity": 10}
>>> thedictionary["item"]
'coffee'
>>> thedictionary["quantity"] += 1
>>> thedictionary["quantity"]
11
Note how the keys are used to reference each item, but that the value is returned; and that, unlike arrays, dictionaries can be referenced with any type.
  • Sets are collections of objects that support standard mathematical set manipulation operations: union, intersection and difference. Consider the examples below:
>>> theset = set(['t', 'r', 'e', 'e'])
>>> theset
set(['r', 'e', 't'])
>>> anotherset = set('flower')
>>> theset & anotherset
set(['r', 'e'])
>>> theset | anotherset
set(['e', 'f', 'l', 'o', 'r', 't', 'w'])
>>> theset - anotherset
set(['t'])
>>> 't' in theset
True
Intersection is represented by the '&' operator, union is represented by the '|' operator, and difference is represented by the '-' operator. The operator 'in' is used to test set membership. Note how sets do not contain duplicate items.

One final word about data types in Python. Python is 'dynamically typed', this means that the type of a variable can change if the value assigned to it changes. Many other languages are 'statically typed', that is, the type of each variable is set when the variable is declared. You might think that dynamic typing can get a little confusing, but each type in Python has a unique notation. For example, lists are specified with square brackets, tuples with parentheses, and sets with the 'set' function.

Operators

Each data type in Python has two properties: the values used for the type and the operators that may be used with values of that type. Below is a brief overview of the operators that can be used with number types, strings and sequences.

Integers and floating point numbers use the standard arithmetic operators. Python includes a '**' operator for exponentiation.

Strings and other sequences support concatenation (the '+' operator) and repetition (the '*' operator). Consider these examples:

>>> listing = [1, 2, 3, 4, "DONE"]
>>> listing + listing
[1, 2, 3, 4, 'DONE', 1, 2, 3, 4, 'DONE']
>>> listing * 3
[1, 2, 3, 4, 'DONE', 1, 2, 3, 4, 'DONE', 1, 2, 3, 4, 'DONE']

The types in Python that involve sequences are characterized by sequential ordering. Sequences are referenced using square brackets to examine individual items in each sequence. Sequences are organized from left to right, as you might expect. Reference notations are offsets from the leftmost item. Python allows some special notation that are shortcuts, as demonstrated below:

>>> drsuess = 'The Cat in the Hat'
>>> animal = drsuess[4:7] # take out the cat
>>> animal
'Cat'
>>> onmyhead = drsuess[15:] # get the Hat
>>> onmyhead
'Hat'
>>> lastone = drsuess[-1] # last character
>>> lastone
't'

Extracts of a sequence are called slices (for strings, you might know these as substrings). Negative index references refer to the end of the sequence.

Sequences can also hold other sequences, as shown below.

>>> drsuess = ["Horton Hears a Who", "Green Eggs and Ham"]
>>> drsuess[1][0:5]
'Green'
>>> drsuess[1][0:5][2]
'e'

It is important to remember that strings are just sequences. In this example the variable drsuess holds a sequence of two items, each of which is a sequence. The double reference drsuess[1][0:5] extracts the second item in the drsuess sequence and then extracts characters 0 through 4 from that sequence, giving the value Green.

Finally, it is important to remember that though Python is dynamically typed, it is also 'strongly typed'. This means that Python enforces the rule that only the operators that are defined for a data type may be used on that data type. Python does not try to figure out how to apply invalid operators; it simply throws an error.

Statements

In Python, as in other languages, a 'statement' is an executable element in the language. It is the element of the language that drives a program's actions.

The syntax of statements in Python is a bit different from other languages. Consider the following Java statement:

while (x < y) {
x += 2;
y = y - 2;
}

This while loop statement has a block of statements, the loop body, surrounded by "{" and "}". The statements within the loop are terminated by a semicolon. The entire statement set is flexible. It could be on one line or many because whitespace characters are ignored by the Java compiler.

In Python, the syntax is cleaner, but more restrictive. Consider the Python equivalent while loop below:

while x < y:
x += 2
y = y - 2

The code is cleaner: the semicolons and brackets of Java are not needed in Python. However, the code is also more restrictive. The block of statements formed by Java's of brackets is formed by indentation. Python relies on indentation to form statement blocks. In addition, individual statement must appear on individual lines - unless semicolons are used. Finally, note the colon that serves as a terminator to the while statement; colons are used when blocks of code are expected.

You can use semicolons to separate statements from each other. The code below is identical to the code above, though it does not look as clean:

while x < y: x += 2; y = y - 2

Consider the simplest statement in Python. It is the 'pass' statement. It does nothing, but is used often as filler for an empty statement block. In a language like Java, an empty block is simply an empty set of brackets: {}. However, in Python an empty block is an indented empty line, which is invisible, so 'pass' is used instead. For example, an empty while loop might look like:

while x<y:
pass

In summary, it is best to say that Python syntax is 'different' but not necessarily better. Python enthusiasts get very eloquent on how the language supports all the same constructs as other languages, but with cleaner code. It is probably truer to say that Python's syntax serves its purpose well ...and leave language comparisons to linguists.

Expressions and Assignments

An expression is a combination of object references and operators. Expressions create and manipulate objects. Consider the expression below:

x + 15 * L[2]

The variable x is referenced and that reference creates an object whose value and data type match the data contained in x. Using the number 15 in the expression causes Python to creates an integer object whose value is 15. The list reference L[2] retrieves the third item in the collection named L and creates another object. All three newly created objects are nameless.

In Python, the above expression is a valid statement: you can use expressions as statements. All expressions return values. Unless the expression causes a side effect the value is thrown away. In the above expression produces no side effects (because no functions are called) so a value is computed and discarded.

As long as they contain operators valid for the data types they are working with, expressions can comprise any combination of objects and operators. Table 2.2 illustrates some common expressions and their interpretation:

Table 2.2: Common Expressions and their Interpretation
Expression Interpretation
x + 15 * L[2] arithmetic expression
a < b boolean expression
x + 15 * L[2] < 100 and a < b compound boolean expression
a < b < c combination of boolean expressions
f(x,y) function call

These should look familiar if you are familiar with how other languages deal with expressions. Precedence rules apply in Python just as they do in other languages. In the first expression, for example, the * operator is executed before the + operator. Boolean combinational operators are spelled out (and , or, and not) rather than specified by symbols as in Java or C++.

Assignment is the operation of changing the object value of a variable. The syntax look just like it does in other languages:

variable = expression

The expression is computed first and the resulting object is assigned to the variable specified.

Here a common mistake in Python which involves function calls, which are valid statements. Consider the following code:

>>> aList = [10,20,30]
>>> aList.append(40)
>>> print aList
[10, 20, 30, 40]

This works as you might expect. The statement aList.append(40) is an expression, but a valid statement nonetheless. It simply returns the value None which is discarded.

The common mistake is to think this expression can be assigned to a new list, like this:

>>> aList = [10,20,30]
>>> aNewList = aList.append(40)
>>> print aNewList
None

Instead, the new list gets the value None, which is the return value from the function call in the expression.

Finally, note that we have used a useful statement in the examples above. The print statement can be used to generate output based on the expression given to it. Python computes the expression and prints the result. If you are familiar with C or C++, this is the same as using stdout. However, we will also use the print statement to work with files later.

Conditional Statements

All programming languages feature the conditional execution of statements; Python is no exception. Python's if statement features multiway branching.

The general format of the if statement is below:

if <condition1>:
<statementset1>
elif <condition2>:
<statementset2>
else:
<statementset3>

Even with no knowledge of Python syntax it looks very similar to an if statement for many other languages. Here, <condition1> is an expression that produces a boolean result. If the resulting value is True the statements in the block called <statementset1> are executed. If the resulting value is False the next condition is evaluated, and so on. If all the conditions evaluate to False, the default block <statementset3> after the else keyword is executed. Note the use of the colon after each condition and the indentation of the statement sets. These are Python standards.

Here is an example:

theString = "The Mad Hatter"
length = len(theString)
if length < 10:
category = "short"
elif length < 20:
category = "medium"
elif length < 30:
category = "kind of long"
else:
category = "long"

In the example the first condition is False, because theString has a length of 14, but the second condition is True. Therefore, the variable category takes the value medium. The remaining conditions are not evaluated.

Boolean expressions in Python resemble those in other languages. They use the standard relational operators (for example, "<" and ">=") and the boolean operators and , or and not. Boolean variables may also feature in boolean expressions. For example, the code below is valid:

theString = "The Mad Hatter"
length = len(theString)
toolong = length > 40
printable = not toolong and length > 10
if printable:
print "The string is OK"

Values other than booleans are also valid as boolean expressions. As in other languages (pioneering in C, continued in C++ and Java), you may use numeric quantities as boolean expressions. When a number is used alone, Python translates it into the expression 'number != 0'. So, the code below is valid:

theString = "The Mad Hatter"
length = len(theString)
if length:
print "The string has characters"

The numeric value may be an integer or a floating point number. In fact, this idea of non-booleans being considered True is extended to other cases:

  • Nonempty objects are considered True.
  • Zero numeric quantities and empty objects are considered False.
  • The special value None is considered False.

Things get a little complicated when combining non-boolean values using boolean operators. Python uses 'short-circuiting' to evaluate boolean expressions. In other words, when the result can be predicted, Python stops evaluating. For example, the boolean expression False and x == 2 can stop immediately after False because False and 'anything' is False. Python stops evaluating and returns the first object it sees that can predict the result of the expression.

Consider this code as an example:

theString = "The Mad Hatter"
length = len(theString)
value1 = length or theString == "The Mad Hatter"
value2 = theString == "The Mad Hatter" or length
value3 = 0.0 or theString == "Mad Hatter"

In this code, value1 takes the value 14. This happens because length is non-zero and thus true, making the boolean expression true. Python stops evaluting and returns that first object that made the expression true. value2 takes the value True, because the comparison is the first true object in the expression and it makes the whole expression have the value true. The variable value3 takes the value False.

Finally, as in C++ and Java, inline if statements are possible in Python. Their form is a little different. Let's take this simple if statement:

if x < 10:
x = 1
else:
x = -1

The inline version of this would be:

x = 1 if x < 10 else -1

This functions like a normal if statement: all the rules apply to the boolean expression and the value assigned depends on the boolean value. Note that the entire right-hand side of the "=" operator is an expression; the entire string x = 1 if x < 10 else -1 returns a single value.

Note.pngCan You Do This?: Using inline if statements, can you write a statement that increments or decrements a variable (say "a") based on another value? If "b == 10" then increment by 2 else decrement by 2. Do this in a single statement.

Loops

Iterative algorithms are handled by two main looping constructs in Python: while loops and for loops. We examine these in this section. Python extends looping by using an iteration protocol specifically designed for lists and sequences. We look at this as well.

Note.pngWhere is the goto?: It has been shown that all programs can be written with sequential execution, if statements and a goto statements. goto is available in many languages (even C++ has a goto statement) and usually takes the form of goto label, where the label is located somewhere in a program. Execution of the program jumps to the statement at the specified label.

Python does not have a goto statement. Goto statements can result in very poor, very unstructured programming. Though some argue that goto statements are very handy — or even essential — most programmers agree that structured programming is better, and that goto statements are not needed when a rich set of looping constructs is available. Python provides such a rich set of looping constructs.

For more on goto statements, see the original work on this subject and a retrospective on the original which is a great review of the subject.

While Loops

The while loop in Python mimics the while loop in other languages and adds Python's special spin on boolean conditions. The general format of the syntax looks like this:

while <condition>:
<statementset1>
else:
<statementset2>

The basic while loop behavior is to execute <statementset1> while <condition> is true. Using this basic construct, however, many different looping forms are possible. Infinite loops are easy:

while True:
print "Help! I'm running away..."

Most loops depend on relational operators:

while x < y + 2:
x += 1
y -= 1
 
while a**2 < b**2 and c+d == e:
a = c - d
b += 1
c -= 2

You might be familiar with the break and continue statements in other languages. Python has them too. break causes the current iteration to stop at the break statement and terminates the loop execution entirely. continue stops the current iteration only and allows the loop to continue execution. Consider the following examples:

a = 20
while a:
a -= 1
if a % 2 == 0: continue
print a

This code prints all the odd numbers from 20 down to 0. The continue statement stops the iteration so that the print statement does not execute for even numbers.

while a**2 < b**2:
if c+d != e: break
a = c - d
b += 1
c -= 2

This code works just like the simple example above. When c+d != e the loop shuts down.

Note.pngSpot the difference: Note the effect of the subtle difference between the two coding styles. Because Python uses short-circuiting to evaluate boolean expressions, the while loop that starts with

while a**2 < b**2 and c+d == e:
...

might never execute the right-hand side of the "and" operator because it will be short-circuited if the the left-hand side evaluates to False. However, if the form:

while a**2 < b**2:
if c+d != e: break
...

is used, "c+d != e" is guaranteed to be evaluated.

This behaviour has a significant impact when we use functions and class methods with side effects. The distinction becomes very important.

The else part of a while loop is rare in Python and a feature that few other languages have. It is optional. If included, it executes when the loop condition evaluates to False and the loop is about to terminate. It only executes when a while loop is terminated in the normal fashion. When a loop terminates with a break statement the else part does not execute. Consider this example:

number = int(raw_input("Enter number: "))
count = 0
while number:
count += 1
total = total + number
number = int(raw_input("Enter number: "))
else:
print "The average is ", total/count

This code reads numbers until a 0 is entered, then prints the average and exits.

For Loops

for loops are useful when you wish to iterate over a specific set of objects — for example, a range of integers or a list. The syntax for a for loop generally looks like this:

for <index> in <objectsequence>:
<statementset1>
else:
<statementset2>

As in other languages, a for loop executes by assigning to <index> an object from <objectsequence> for each iteration of the loop. For example:

thesum = 0
for x in [1, 2, 3, 4, 5, 6]:
thesum += x
print "Average is ",thesum/6

The output is "Average is 3" as you might expect.

We could write the above using a tuple instead of a list:

thesum = 0
for x in (1, 2, 3, 4, 5, 6):
thesum += x
print "Average is ",thesum/6

Any sequence works in a for loop. The example below shows a for loop working with a sequence of tuples and tuple assignment.:

thetuple = [(10,3.14), (20, 14.5), (30, 17.9), (55, 2.31)]
thesum = 0
for (left,right) in thetuple:
print "Reading ", left
thesum += right
print "Average of lefts is ",thesum/len(thetuple)

This produces the following result:

Reading 10
Reading 20
Reading 30
Reading 55
Average of lefts is 9.4625

This syntax is a little different from Java and C++. for loops in C++ and Java have a slightly obtuse syntax. They are usually used to provide integers for use as an index to step through an array or list. Python does not support that syntax - the example above is the only way to use for loops. However, there is a way to generate ranges of integers in lists easily.

Consider this example. In Java, we might have a list of objects that we want to process:

for (int i=0; i < objectlist.length; i++) 
process(objectlist.get(i));

To make a similar loop in Python, we need to use the range() function. The Java syntax gives us a counted iteration, where the loop index variable takes values from 0 to objectlist.length. The range() function creates a list with a sequence of integers in the range given by the function parameters. A loop in Python similar to the Java loop above might look like this:

for i in range(len(objectlist)):
process(objectlist[i])

The range() function here produces a list of all integers from 0 to objectlist.length in sequence. The loop variable takes each of these values as the loop iterates, acting just like the Java for loop.

You can specify a starting number and a step as below:

for x in range(10,100,5):
...

This iterates for values of x starting at 10 in steps of 5 through 100.

Note.pngfor loops as while loops: Remember that for loops are really while loops. Anything you can do in for loop can be done in a while loop.

As with while loops, the 'else' part executes when the loop terminates normally. Also, break and continue statements work with for loops as they do with while loops.

Other Python Iterators

for loops in Python are very powerful statements. They work with sequences, but they can do other things too. They can be used with any 'iterable' data type, that is, any data type that can generate a sequence of values.

The notion of an "iterable" data type capitalizes on the idea that if you have one object of a data type, you can get the next. For integers, this is easy: if you have an integer "x", the next value is "x+1". For a list, if you are at the fifth value in a list, the next one is the sixth (i.e., 'list[5]'). Python uses these ideas for all iterable data types and specifies a syntax for iteration.

Consider the example below:

countries = ["France", "Germany", "United States", "Austria", "Norway"]
iterator = iter(countries)
print iterator.next()

This code creates an iterator for the countries list and begins to step through the values in the list using that iterator. The result of the print statement is 'France'. Another call to iter(countries) will produce 'Germany'.

Some data types have iterators built in. For example, file types have iterators:

fd = open("data.txt")
print fd.next()
print fd.next()
print fd.next()

This snippet prints the first three lines in the file "data.txt" (the function fd.readline() does the same, but we are not discussing files here).

The enumerate() function is a variation on range generation and is useful for iteration in for loops. The enumerate() function takes a list and generates an iterator over list as tuples: the first item in each tuple is an integer representing the place of the second item in the original list. Consider the example below:

countries = ["France", "Germany", "United States", "Austria", "Norway"]
for (offset, country) in enumerate(countries):
print country, 'is the', offset, 'item in the list'

Again, the use of the enumerate() function produces an iterator, not a list. We could have simply used "iter(countries)", but we would not have the offset included in a tuple. This fragment produces the result:

France is the 0 item in the list
Germany is the 1 item in the list
United States is the 2 item in the list
Austria is the 3 item in the list
Norway is the 4 item in the list

Here, we should make a note: 'for loops work with iterators as well as with sequences'. Working with sequences may seem to be their primary duty, but the fact that they work with iterators means they are very flexible.

There are some variations on for loops and iteration that might look a little strange at first. For example, if you want to generate a list of the powers of 2 between 1 and 5, you might try:

powers = [2**1, 2**2, 2**3, 2**4, 2**5]

This is fine for short lists. However, if you wanted to generate a list of the powers of 2 between 1 and 32, you might do this:

powers = [2 ** x for x in range(32)]

This type of inline for loop is much easier to use and more convenient.

Functions

A function is a collection of statements, treated as a special unit, that can be used many times in a program. Using the group of statements in a function is known as calling the function. When one part of a program calls a function, it can pass data to the function for the function to use; these data are known as parameters. As part of their execution, functions can return values back to the calling statement (but are not required to do so).

We have already seen functions at work. In previous examples, we called the range() function, passed it an integer parameter, and used the list of integers that it returned. We also used the next() function for iterators and the enumerate() function. If you have used other programming languages, calling functions should be very familiar.

In this section, we focus on writing user-defined functions and explaining how the various semantics of functions affect this type of function.

Writing User-Defined Functions

User-defined functions are written using the def statement. The general form of this statement is below:

def <function name>(<parameter list>):
<statementset>

The '<function name>' should take the form of all other names in Python: it must start with a letter and can be made up of letters and numbers after that. The '<parameter list>' is optional and is a list of names that will be used to reference the parameters that are sent during a function call. The '<statementset>' is the block of statements, appropriately indented, that executes when the function is called.

Consider the simple example below:

def listaverage(aList):
thesum = 0
for x in aList:
thesum += x
print "Average is ",thesum/len(aList)

This is a repackaging of an earlier example. We can call this function by executing the statement:

listaverage([1,2,3,4,5])

and we get the output:

Average is 3

In the definition, the list '[1,2,3,4,5]' is assigned to the parameter aList and that parameter is used as a variable in the defining code.

Note.pngCoding Habits and Discipline: It is natural for programmers to develop habits when writing programs. Some like to use a certain style of naming for functions and parameters. Language designers might even use a certain style in designing a language to help programmers write better code. For example, the 'declaration before use' rules of C++ and Java are designed to force programmers to be intentional about using variables in those languages.

It is important in a language like Python to develop and maintain good coding habits. The language design is loose and rarely enforces a specific kind of coding practice. Therefore, it is up to you to use good practices.

For example, because you may use any names as parameters and variables, it is easy to get them mixed up in a function definition. However, good coding discipline should ensure that you can identify them. Notice that the name of the parameter in the example above begins with the letter 'a' followed by a capital letter. This is to distinguish it as an 'argument' to the function.

Function definitions are 'executable' and are created at program runtime. This happens because of the interpreted nature of Python. This means that the def statement can occur anywhere any other statement can occur. You can also treat the function name just like a variable and assign it to other names. Consider this example:

if x < y:
def announcement():
print "x is indeed less than y!"
else:
def announcement():
print "Sorry, x is not less than y!"
 
shout = announcement
shout()

The result of this code fragment, when x is "1" and y is "2" is:

x is indeed less than y!

This might be obvious, but the way the definitions work is unique to Python. Definitions can be nested inside an if statement because they are executable and function objects can be assigned to other names because executing a function definition produces a function object. This may be a little contrived for this simple an example, but these mechanisms are useful for more complex programs.

Function parameters

Python function parameters are specified as a comma separated list of zero or more named parameters (or "arguments"). When the function is called the parameters can be passed either unnamed in the order of the function definition, or in any order using their names. In the example below a function which takes three parameters and adds them is called using both named and unnamed parameters:

def adder(a,b,c):
return a+b+c
 
x=1
y=2
z=3
 
#specify parameters in original order of function definition (unnamed)
print adder(x,y,z)
 
#specify values of named parameters
print adder(b=x,c=y,z=a)

We can declare default values for parameters in the function definition using the assignment operator. The combination of default values and named parameters gives us a lot of flexibility. In the example below we set the non-default parameter explicitly, without having to define the other parameters.

def adder(b,a=3,c=0):
return a+b+c
 
x=1
print adder(b=x)

Tip.pngTip: Care should be taken when assigning mutable default parameters. A default parameter, like every other named object in Python, is a reference to an object. If the object is mutable its value can be changed in your executing program


The examples above are great if we only want to add zero, one, two or three numbers, but what if we want to add an arbitrary number of numbers? One way would be to use a list or tuple as an argument. Another is to use Python's special syntax to specify a variable number of arguments:

  • a single asterisk (*) in front of a parameter is a tuple of all remaining unnamed values.
  • a double asterisk (**) in front of a parameter is a dictionary of all remaining named parameters.

If both single and double asterisk arguments are specified, the double asterisk must be specified last.

The example below shows an add function that uses the single asterisk syntax to add any number of unnamed parameters.

def add(firstValue=0,*myArgumentList):
sum=firstValue
for number in myArgumentList:
sum+=number
return sum
 
print add(1,2,3,4,5)


Python passes parameters into a function body as object references. This means that if an object passed as a parameter is of a mutable type (like a list, set or dictionary) changes to it within the function affect the referenced global object. If the object is immutable (like a tuple, string or integer) it cannot be modified by the function. If the function attempts to modify an immutable object type Python creates a local scope copy of the object for use within the function.

Care must be taken to ensure that you don't accidentally modify mutable variables within your function. If the intention of passing a mutable object is simply to assign values to it within the function, a safer and more "Python" approach is to assign the result of your object to the return value of the function (as discussed in the following section).

Returning values

Python functions can return a value (or values) back to the caller by executing the 'return' statement, as shown below. This mechanism for returning values is preferred over modifying the values of mutable function arguments.

return <expressions>

The <expressions> are computed, the function execution is terminated, and the value is returned to the caller. That value can then be used as any other value or variable reference. Note that if no value is explicitly returned, a function will return None.

Consider the code below, a proper rewriting of the listaverage() function:

def listaverage(aList):
thesum = 0
for x in aList:
thesum += x
return thesum/len(aList)

Now a call to listaverage() prints nothing, but returns the average and can be used in an expression or other statement.

Functions in Python can return multiple values. For example:

def listaverage2(aList):
thesum = 0
for x in aList:
thesum += x
return len(aList),thesum/len(aList)

This function returns both the length of the list and the average of the numbers. We can use it like this:

l,a = listaverage2(range(32))
print "l is ", l, " and a is ", a

The result is:

l is 32 and a is 15

Scope Rules

As we have seen, Python has the notion of 'statement blocks' — sets of statements, grouped by indentation. Identifier names for variables and other objects can be used in these blocks without declaration. Blocks can be nested inside each other and then the names in use can become confusing.

What is the value of a that gets printed in the example below?

a = 100
def f():
a = 99
f()
print a

Python has scope rules which define the visibility of identifiers.

To understand the scope rules of Python, we need to understand some definitions. We first need to make the distinction between 'global scope' and 'local scope'. Global scope is the space of identifiers for an entire Python module or program. All blocks at all levels in a Python program can see and use global names. Local scope is the space for identifiers in the innermost function definition. Between global and local there is 'enclosing scope'; this is the space of identifiers defined outside of a statement block. And there is one more scope: all built-in Python identifiers are available to all parts of a Python program.

Figure 2.1 shows the relationships between these scope areas. This set of rules is often called the 'LEGB scope rule'.

Figure 2.1: Python Scope Areas

Let's make a couple of observations on this LEGB rule before we get into an example.

  1. Since we do not declare variables in Python, the operation that establishes the block where a name is defined is 'assignment'.
  2. When a name is used, the LEGB rule defines the lookup sequence that Python uses. Python starts looking for an assignment in the local block, then in the enclosing blocks, then in the global block, then finally in the set of built-in Python definitions.
  3. A name may be declared global, which places it into the global scope area.

Here are some examples.

a = 100
def f():
a = 99
f()
print a

This is the example that opened this section. What is the value of a that gets printed? To figure this out, we need to know which a the print statement refers to. There are two a definitions, one in the f() function block and one in the global program block. The lookup rule starts in the current block and looks "outward" to outer blocks, so the a is from the global block, which has the value 100. Note that the function changes its own value of a, so it does not affect the global definition of a.

a = 100
def f():
print a
f()
print a

In this example, two values are printed, both refer to global name a. The first print statement needs to find an a to print. The search begins in the local scope, but no assignment defines an a in that scope. So, the search moves to the enclosing scope (which happens to be the global scope as well) and there is an a defined there with the value 100. Both print statements reference this definition of a.

a = 100
def f():
print a
a = a+100
print a
f()
print a

This code actually produces an error. The inner scope of the function needs a definition of a to print. The Python interpreter looks for the definition in the function's scope area and finds it in the assignment statement after the print statement. This results in an error because the variable is referenced before it has a value. The interpreter might have used the name from the enclosing scope, but the rule is to use the local scope first.

a = 100
def f():
global a
print a
a = a+100
print a
f()
print a

This code is identical to the previous code but with the addition of a global declaration. Now it works. Because of the global declaration, the assignment statement in the function uses the global definition of a instead of attempting to create a new one.

Here is a final note about parameters to functions. Parameters are passed to functions as if they were assigned to the local function names of the parameters. This means that parameters are in the local scope area of a function. This means that changing the values of parameters has no effect on definitions in enclosing scope.

Note.pngRamifications of Scope Rules: Redefinition of Names: There are many ramifications of scope rules; some are more obvious than others. One ramification is that you can redefine names from an outer scope in an inner scope.

You should avoid such redefinitions. The biggest reason is that you will hide the outer definition by the inner definition, making the outer definition inaccessible. For example:

def min(a,b):
return a if a>b else b
min(2,3)

This is the wrong definition of a min() function; it is actually a max() function. However, once this is defined, it will be used in place of the built-in min() function and always return the wrong result.

Another reason why redefinition is not useful is that it can get confusing. Constantly redefining the variable mileage, for example, might result in some very complex code, especially if you need to track down a bug that sets the variable incorrectly.

lambda Functions

There is a way to define a function in Python without giving that function a name through the def statement. This type of anonymous function is defined using a lambda expression.

A lambda expression allows you to express parameters and an expression that defines its return value. The general form looks like this:

lambda <parameterlist>: <expression>

For example, we could use a lambda function like this:

func = lambda a, b, c: (a + b + c) / 3
x = func(1,2,3)

In the code above, we define an anonymous function that returns the average of three numbers. That definition is assigned to a variable and that variable is used to invoke the function. At no time did that function get a name, so if the variable func were to be reassigned, the definition of that function would be lost.

We should note some properties of lambda functions. First, the use of the lambda function is an expression, not a statement. This means that the definition of the lambda function returns a value — a function value — that can be assigned to a variable as we did above. Second, the body of a lambda definition is an expression, not a set of statements. This means that is not as flexible as a def definition.

However, lambda functions can be used conveniently in several situations. They are simpler code constructs than using a def statement. We will see them in use in later chapters as callback functions. One common use of lambda functions is as a 'jump table'. A jump table is typically a dictionary construct with lambda functions as values.

Consider this example:

table = {'green': (lambda: x * y),
'orange': (lambda: x + y),
'blue': (lambda: x - y + 2),
'yellow': (lambda: x / y -1)
}

Now we have a table of code. Assume we have a color in the variable color. To reference the function that corresponds with that color, we would reference:

table[color]()

This is much more convenient that a series of if statements.

Classes and Objects

We have talked about the various components of object-oriented programming and now it is time to define how Python embraces it.

Object-oriented programming has several components:

  1. Objects: An object is the centerpiece of object-oriented programming. An object wraps a data component with operations on that data as defined by a class.
  2. Classes: A class is a template for an object. It consists of descriptions of the operations an object can perform and the definition of the structure of the object's data component. Classes exist in a hierarchical structure to permit inheritance to take place. If one class is a subclass of another, it inherits the data structures and operations from the parent.
  3. Methods: Methods are the operations that can be performed on a data component in an object.

So an object is a combination of data structures and methods that work on those data structures, as defined by a class. Python permits each of these components to be defined and used. In Python, objects that are used for execution are called 'instances' and are built from class definitions.

Defining Classes

The general form of class definitions in Python looks like this:

class <name>(<superclass>,...):
<shared data section>
<method section>

That looks very generic, here is an example.

class Account():
def __init__(self, aStartingAmount):
self.amount = aStartingAmount
 
def deposit(self, aDepositAmount):
self.amount += aDepositAmount
return self.amount
 
def withdraw(self, aWithdrawlAmount):
self.amount -= aWithdrawlAmount
return self.amount
 
def balance(self):
print self.amount

This is a class called Account that represents a bank account. It has a data object called amount which is initialized with the value aStartingAmount in the __init__() method. When an instance object is created, the __init__() method — called a 'constructor' — is called if it exists. The creation of an instance must specify values for the parameters specified by this constructor.

Method definitions are just function definitions. We have already reviewed functions, so you know about methods!

Each method in the above definition has at least one parameter. This parameter refers to the instance of the class that has been created. We use this instance above in references to data. When we use self.amount, for example, we are referring to the amount variable that exists in a specific instance of this class, referred to by self. Programmers in C++ and Java will recognize the self variable as the this object.

We can use this class as follows:

bankaccount = Account(1000)
bankaccount.balance()
bankaccount.deposit(250)
bankaccount.withdraw(100)
bankaccount.balance()

The first line of the code creates an 'instance object' called bankaccount of the 'class object' called Account. We initialize the amount of the bankaccount object to 1000 by including it in the instance creation statement. This calls the constructor and passes it the value 1000.

The remainder of the example above uses methods from the class. The results of running the above code is below:

1000
1150

Inheritance

Inheritance occurs when classes are defined in terms of other classes and take their data components and methods. This establishes a relationship between classes that can be exploited for software reuse. Inheritance is one of the most useful and important concepts of object-orientation and Python supports it fully.

Let's say that we want to define two more banking classes: CheckingAccount and SavingsAccount. We could go through the same definitions as we did with the Account class and each of three classes would have their own definitions. However, it would be advantageous if these classes had a relationship: if we could use one of these new classes in the same places as Account. In fact, a CheckingAccount is exactly the same as an Account, and a SavingsAccount is an Account with a special definition for withdraw that does not allow amount to go below $100.

We can define these subclasses like this:

class CheckingAccount(Account):
pass
 
class SavingsAccount(Account):
 
def withdraw(self, aWithdrawalAmount):
if (self.amount-aWithdrawalAmount > 100):
self.amount -= aWithdrawalAmount
return self.amount

In this definition, CheckingAccount has the same definition asAccount and therefore only includes the 'pass' statement (class definitions need a block of statements). SavingsAccount is defined in terms of Account, but redefines the withdraw() method.

Here, anytime an Account is required, we can use CheckingAccount or SavingsAccount, because they inherit the Account type. For example, let's write a function that transfers money between two accounts:

def transfer(aAmount, aAcct1, aAcct2):
aAcct2.deposit(aAcct1.withdraw(aAmount))

This code would work equally well with Account, CheckingAccount, or SavingsAccount.

Often the hierarchical nature of the inheritance relationship is exploited to provide a programming interface of "expected" method implementations. This way of providing an interface assumes that a method exists or provides a method but leaves its implementation empty.

Let's redefine the Account class as below:

class Account():
def __init__(self, aStartingAmount):
self.amount = aStartingAmount
 
def deposit(self, aDepositAmount):
self.amount += aDepositAmount
return self.amount
 
def withdraw(self, aWithdrawalAmount):
if (self.amount-aWithdrawalAmount > self.limit):
self.amount -= aWithdrawalAmount
return self.amount
 
def balance(self):
print self.amount

This code now has the withdraw() method based on a variable called self.limit, which is not defined by the Account class. This makes the Account class unusable for regular programming and turns it into an 'abstract class', one that is used as a base class to define other classes and to force those classes to have a specific definition. Now, to be usable, both CheckingAccount and SavingsAccount must define self.limit. Here is the new definition of these classes:

class CheckingAccount(Account):
def __init__(self, aStartingAmount):
self.amount = aStartingAmount
self.limit = 0
 
class SavingsAccount(Account):
def __init__(self, aStartingAmount):
Account.__init__(self,aStartingAmount)
self.limit = 100

Both definitions define a self.limit variable that is assumed by the Account definition.

Notice how the above definitions have different ways of using the parent class. The CheckingAccount definition rewrites the __init__() method completely; the SavingsAccount definition calls the parent class' __init__() method before making refinements. The second approach is better because it does not assume a definition in the parent class. If the __init__() method changes in the parent class the SavingsAccount definition will still be correct, whereas the CheckingAccount definition will not.

Multiple Inheritance

We should make a note here about Python's ability to use multiple inheritance. We discussed inheritance and showed how a subclass can inherit the properties of a parent class. In Python, this inheritance relationship can extend to multiple parent classes.

Multiple inheritance is useful when a class needs access to the data objects and methods of more than one class. Let's say that a Python program has a set of definitions that define, among other objects, an Employee class and a Student class. There might be a class that defines student employees, which would have to inherit both Employee and Student class properties. Again, let's say that a user interface has a class that depicts items on a menu and buttons on a page. There would also be classes that inherited aspects from both of these classes to put buttons on items on menus.

Muliple inheritance is specified in Python by putting multiple classes in the class statement. For example, we could define:

class MenuItemWithButton(MenuItem, Button):
...

The MenuItemWithButton class has two parents and must implement the proper abstract properties from both.

It can happen that some of the classes that are inherited have properties with the same name. In the above, both MenuItem and Button classes could have a method named isPressed or setVisible. The rule for which one is referenced is the "left to right" rule: the classes specified in the class statement are searched in specification order left to right. So the setVisible method from the MenuItem would be used for that method unless it is redefined for the new class.

Note.pngMultiple Inheritance Considered Controversial: Like some other language features (e.g., the goto statement), multiple inheritance is a controversial feature. Some languages (C++, Python) include it while others (Java,C#) specifically exclude it. Proponents find it useful and actually necessary to properly depict certain object-oriented relationships; detractors find it confusing and not needed for most programs. Some languages (Java) implement multiple inheritance, but cloak it behind language features designed to encourage better programming (in Java, the interface concept is an attempt to incorporate single inheritance with a cleaner multiple inheritance implementation).

Operator Overloading

One specialized way of working with class methods is through operator overloading. It is possible to 'overload' — or redefine — operators in Python classes. This applies to the basic common operators used in the built-in data types as well.

For example, consider the following definitions:

class MyNumber:
def __init__(self, aInitialValue):
self.value = aInitialValue
def __add__(self, aValue):
return MyNumber(self.value + aValue)

Now, the operator "+" can be used with instances of the MyNumber class. We can now execute the following:

x = MyNumber(100)
y = x + 100

y.value now has the value "200". Whilst that might not be surprising, the beauty of the code is the we used the "+" operator. This is a nice way to provide intuitive operators when classes demand it. If a class denotes a collection of objects, for instance, redefining the "[]" operator would be an intuitive thing to do.

Almost all operators in Python can be overloaded in this manner. See the Python documentation for a list.

Note.pngOverloading Accounts: It would make a lot of sense to use "+" as deposit() and "-" as the withdrawl() definition in the Account definitions above. Can you show how these definitions would incorporate these operators?


Exception Handling

Exception Basics

Exceptions are events which define a condition that has arisen that needs to be dealt with. These events are typically errors that occur during the execution of a program, but exceptions can also be used in other situations to modify the execution of a program. The key feature of an exception is the need for it to be handled. Unhandled exceptions halt the execution of a program.

Python has a rich set of language constructs that implement exceptions. The most basic of these features is the try/except sequence of statements. This is the general format:

try:
<statementset1>
except <name1>:
<statementset2>
except <name2>,<data>
<statementset3>
except (name3, name4):
<statementset4>
except:
<statementset5>
else:
<statementset6>
finally:
<statementset7>

Using exceptions is very flexible. Python starts by attempting (trying) to execute <statementset1>. If these statements execute without raising an exception the statements in the optional else part — <statementset6>— and finally part — <statementset7>— are executed. If an exception is raised, however, the except parts are searched for the <name> of the exception. When the matching exception is found the statements under that except part are executed. If the name of the exception is not found, the statements under the generic except part — <statementset5> — are executed. The except statement can be used with the "<data>" element to get more information about the exception. The finally part is always executed.

Consider an example. A very common programming error is division by zero. This has an exception associated with it:

a = 10
b = 0
try:
c = a/b
except:
print "Oops...something bad happened"

This code produces the message 'Oops...something bad happened' and it should be obvious why this exception was raised. In Python, this exception has a name: 'ZeroDivisionError'. We can look for this exception specifically with the following code:

a = 10
b = 0
try:
c = a/b
except ZeroDivisionError:
print "Oops...something bad happened"

This code produces the same result. Extra information can be obtained by adding a variable to the except part. In our simple case, this extra information is an error message that explains the error:

a = 10
b = 0
try:
c = a/b
except ZeroDivisionError, message:
print "Oops...something bad happened"
print message

This code produces the output

Oops...something bad happened
integer division or modulo by zero

Let's conclude this simple example with an else statement.

a = 10
b = 10
try:
c = a/b
except ZeroDivisionError, message:
print "Oops...something bad happened"
print message
else:
print "Hey! Something good happened"

This code prints the message in the else statement, because the division completes successfully.

Because of the way that the try statement is structured multiple exceptions can be handled and common ways to complete the statement can be implemented. Multiple exceptions can be handled in the same way by naming them all in the except part.

There are many built-in exceptions in Python, too many to list here. They all have names and depict a specific kind of error condition. Python always prints the name of the exception when giving an error message. So, if you were use a division by zero in a Python program, you might get an error message like this:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

Note the name of the exception in the last line.

Raising Exceptions

There are lots of built-in exceptions, but you can also raise your own exceptions. This is a useful feature when you want to force certain conditions (like errors) to be handled by programmers using a programming interface. You raise exceptions with the raise statement. It can be used in several ways:

raise <exceptionname1>
raise <exceptionname2>,<value>

These should make some sense given the way that we use the try statement to catch exceptions. The first form is the the more obvious; it raises the named exception. The name can be a built-in exception, a string that you make yourself, a class name or an an instance.

The second form adds a string value that serves as additional data. The string is usually an error message that augments the exception.

Let's take a couple of examples. Consider an addition to the Account class definitions above:

BalanceError = 'BalanceError'
 
class Account():
def __init__(self, aStartingAmount):
self.amount = aStartingAmount
 
def deposit(self, aDepositAmount):
self.amount += aDepositAmount
return self.amount
 
def withdraw(self, aWithdrawalAmount):
if (self.amount-aWithdrawalAmount > self.limit):
self.amount -= aWithdrawalAmount
else:
msg = "Account is overdrawn by "+str(-(self.amount-aWithdrawalAmount))
raise BalanceError,msg
return self.amount
 
def balance(self):
print self.amount
 
class CheckingAccount(Account):
def __init__(self, aStartingAmount):
self.amount = aStartingAmount
self.limit = 0
 
class SavingsAccount(Account):
def __init__(self, aStartingAmount):
Account.__init__(self,aStartingAmount)
self.limit = 100

We have added several things. We now have a global string that identifies 'BalanceError'. We will use this as our exception. We also added an 'else' part to the withdraw() method that creates an error message in the variable msg and raises the BalanceError exception. Now we can use the following code to catch this new exception:

newacct = CheckingAccount(100)
try:
newacct.withdraw(200)
except BalanceError,errormsg:
print errormsg

Exceptions and Propagation

Exceptions must be handled somewhere or the program will terminate. Python 'propagates' exceptions: It passes them through the calling sequence and any of the functions in the sequence can handle them.

For example, if a function 'funcA' calls 'funcB', which calls 'funcC', we have a calling sequence like that in Figure 2.2:

Figure 2.2: Calling Sequence Implies Exception Propagation

Let's say that an exception is raised in 'funcC'. If there is an exception handler in "funcC" Python uses that. If not, it looks for one in "funcB" and so on up the calling chain.

Importing Modules

It would be awkward if every Python program were a single file. Python programs depend on many built-in definitions and on code written by others. So it makes sense to split code definitions up into multiple files. This is where importing modules comes in.

Python program file names end in a '.py' suffix and are referred to as 'modules'. A program can reference items from other modules by 'importing' them. The contents of modules are known as 'attributes' and can be imported as a large unit or individually.

Here is an example. Let's assume that the Account class definitions above, the child classes CheckingAccount and SavingsAccount, and the BalanceError exception, are stored in a file called 'Account.py'. If we create a new file with an instance of the SavingsAccount class we must import the definition from 'Account.py' like this:

import Account
myaccount = Account.SavingsAccount(1000)
myaccount.balance()

The import statement made the attributes defined in the 'Account.py' file available to the current program. We reference the module's attributes — in this case, the SavingsAccount class — using the same dot notation we use to reference class attributes.

We can avoid this dotted notation and be a little more specific in our importing by using the from keyword with the import statement, as below:

from Account import SavingsAccount
myacct = SavingsAccount(1000)
myacct.withdraw(2000)

Executing the last line, however, raises an exception:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "Account.py", line 17, in withdraw
raise BalanceError,msg
BalanceError: Account is overdrawn by 1000

Importing module attributes can get a little tricky. Let's say that we want to trap the BalanceError exception in try statement, like this:

from Account import SavingsAccount
try:
myacct = SavingsAccount(1000)
myacct.withdraw(2000)
except BalanceError, errormsg:
print errormsg

We get an error here too:

Traceback (most recent call last):
File "accttest.py", line 6, in <module>
except BalanceError, errormsg:
NameError: name 'BalanceError' is not defined

This is because we imported the SavingsAccount attribute, but no others. We referenced the BalanceError name without importing it. We can get around importing every attribute individually by using the following syntax for the import statement:

from Account import *
try:
myacct = SavingsAccount(1000)
myacct.withdraw(2000)
except BalanceError, errormsg:
print errormsg

By using 'import *', we import every attribute and do not have to use the dot notation to reference them. This is the most commonly used form of the import statement.

We can also import an entire directory of files and definitions. This called a 'package' import. It is a somewhat advanced feature of Python, however, so we do not describe it here.

Miscellaneous Python Elements

As we conclude our brief overview of Python, there are a couple of miscellaneous items we should mention.

The first is docstrings. Docstrings are a special form of string that can be used in program files and function definitions. They are treated like comments at execution time and ignored. However, they are used and displayed by tools that work with Python, including the Python runtime environment. Docstrings are surrounded by three quote marks, as you can see below:

def sum(a,b,c):
'''
This is a contrived example to show
off docstrings
'''

return(a+b+c)

When the Python runtime system parses a docstring, it stores it as the __doc__ attribute for the item being defined. We can print documentation by using the following code:

>>> print sum.__doc__
 
This is a contrived example to show
off docstrings

We can print documentation like this for built-in definitions too. For example, the function max() is built into the Python runtime system and we can read its documentation as follows:

>>> print max.__doc__
max(iterable[, key=func]) -> value
max(a, b, c, ...[, key=func]) -> value
 
With a single iterable argument, return its largest item.
With two or more arguments, return the largest argument.

The help() function is built into the Python runtime system and is defined to print docstrings if they exist.

This brings us to our second miscellaneous item. The dir() built-in function allows us to explore the runtime environment and determine the definitions that currently exist. The function has two forms: without parameters, it prints the names of all definitions; with one parameter, it prints the attributes of the name given by that parameter.

For example, if you want to see all the definitions built into the sum() function we described above, you could call dir(sum). You would get something like this:

>>> dir(sum)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

Usually, definitions that begin and end with the "__" character sequence are system-defined. All of these definitions can be used in Python code.

Note.pngExplore!: We cannot go over all of these attributes in this chapter. So, go ahead and explore these yourself. Try using the help() function on these, like help(sum.__init__). You will get several screens of documentation.

Finally, a note about program formats and lines. As you now know, Python is designed to have one statement per line and to use indentation to group statements together. This means that if you write a statement that takes multiple lines, Python might mistake the extra lines as a new block and find the code to be in error. To write statements that span multiple lines put a "\" character at the end of lines that are continued.

For example, this code is in error:

if (x < y and
y > z and
a > y):
...

but this code is fine:

if (x < y and \
y > z and \
a > y):
...

Summary

This chapter has provided a brief overview of Python's basic elements, covering many of the most import aspects of the language:

  • Variables, along with data typing and operations on data
  • Expressions and assignment operations
  • Statements, including conditional statements and iterative looping constructs, with an introduction to Python iterators
  • Functions, including parameter passing, scope rules, and lambda functions
  • Object oriented features, including classes, inheritance, and operator overloading
  • Exceptions.

While many of these concepts can be found in other programming languages, Python provide has a unique and developer-friendly perspective on them.

Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. Portions copyright Bogdan Galiceanu, Hamish Willee, Marcelo Barros de Almeida, Mike Jipping, Pankaj Nathani and others in wiki document history list. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 8 May 2013, at 14:58.
96 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×