Python is a free, open-source, mature, portable, efficient, well-documented, general purpose programming language, with well-designed libraries and frameworks to support Web programming.
It is used for implementing some of the Web's largest applications, e.g., many of Google's applications are implemented in Python.
The current version of Python is 3.2. Versions 3.x are backwards incompatible with versions 2.x; Django is implemented in 2.x; it does not run on 3.x. The most recent 2.x version is 2.7, but we shall use only version 2.6 in this course.
Perhaps the two most important references for us are the tutorial and the library reference. (The language reference is sometimes useful but is written in a very terse, formal style.)
Python is a typical modern, dynamically-typed, object-oriented language with Java-like control structures and an extensive library to support Web programming.
"Dynamically typed" means that unlike Java you don't have to declare the types of each variables and that the same variable may have values of different types at different times.
Python is "strongly typed" in contrast to PHP which is "weakly typed". Hence, in Python a string is a string is a string, whereas, in PHP, a string is sometimes a string and sometimes an integer and sometimes a boolean (e.g., "123" + 3 = ?). This makes Python more predictable than PHP.
Statement nesting in Python is indicated by indentation. I.e., leading spaces are significant. Use 4 characters as the indentation amount. Set your editor to do all indentation with spaces, not with tabs. Learn to use your editor's indent and outdent shortcuts. Do not mix spaces and tabs.
Python has the usual primitive data types - integers, booleans, characters, etc. - and a useful set of compound data types - tuples, lists (which replace arrays), dictionaries (which correspond to Java maps and PHP associative arrays), sets, and classes.
The Python interpreter can be run from the (dwarf) command line, by typing
$ python
followed by Python expressions which are evaluated, statements which are
executed, or definitions which are stored.
Alternatively, you can load a Python module, e.g.,
mymodule.py, by typing
$ python mymodule.py
A Python module is a file containing definitions and statements. The most
common definitions are functions and classes. To include a module so that
the defined functions and classes can be used, use an import
statement:
$ python >>> import module >>> use module.name
or:
>>> from module import name >>> use name
Python functions may have normal, positional arguments, or keyword
arguments, or keyword arguments with default values. For example (from the
tutorial), suppose module fibo.py contains:
def fib(n):
"""Print the Fibonacci sequence up to n."""
a, b = 0, 1
while b < n:
print b,
a, b = b, a+b
print
Then we can call the function fib as follows:
$ python >>> from fibo import fib >>> fib(100) 1 1 2 3 5 8 13 21 34 55 89
The definition of function fib illustrates several characteristic
features of Python:
Strings. Strings come in two forms: ASCII strings (the default)
and Unicode strings. (Python 3 uses Unicode strings as the default.) Use
a single quote character (' or ") for single-line strings
and a triple quote character (''' or """) for multi-line
strings. The substitution operator % is often useful,
particularly for output, e.g., "the sum of % plus % is %" %
(1,2,3) evaluates to "the sum of 1 plus 2 is 3". This is
very useful for formatting output. It is possible to access single
characters strings and slices from strings, as with lists below. There are
many functions for operating on strings, e.g., s.lower(),
s.upper(), s.find(sub), s.isalpha(),
s.isdigit(), s.join(iterable), s.split(),
s.strip().
Regular expressions. A regular expression is a pattern
constructed from strings using alternation, repetition and selection. They
are very useful in recognising particular classes of strings, such as those
denoting postcodes, phone numbers, email addresses, URLs, and so on. An
example of a pattern intended to match Australian phone numbers is
"(0\d)?\s*[1-9]\d{3}[\s]*\d{4}". Functions for operating on regular
expressions are in the module re. The most useful regular
expression functions are compile(pattern),
search(pattern, string), match(pattern,
string) and split(pattern, string).
Regular expressions are used to define URLs in
Django.
Lists. A list is an unbounded sequence of values of possibly
different types, e.g., ["this", "is", "a", "list", "of",
"length", 7]. One can access single list elements (e.g.,
list[0]) and list "slices" (e.g.,
list[0:len(list)], list[0:1], list[5:]). One
can update single list elements (e.g., list[0] = "first")
and list slices (e.g., list[3:4] = [1,2,3]). There are
many functions for operating on lists (e.g.,
list.sort()).
To iterate over a list, use a for-statement:
>>> for x in list: ... print x,
The function range(from,to[,step])
also returns a list, and is useful for arithmetic iteration:
>>> range(1,10) [1,2,3,4,5,6,7,8,9] >>> for n in range(1,10,2): ... print n, ... 1 3 5 7 9
List comprehensions. These provide a simple, clear way to construct new lists from existing lists. For example (from the tutorial):
>>> freshfruit = [" banana", " loganberry ", "passion fruit "] >>> [fruit.strip() for fruit in freshfruit] ["banana", "loganberry", "passion fruit"] >>> list = [2, 4, 6] >>> [3*x for x in list] [6, 12, 18] >>> [3*x for x in list if x > 3] [12, 18]
(List comprehensions should be very familiar to any Haskell programmers.)
See Section 5.1 of the tutorial for more information about lists.
Tuples. A tuples is a fixed-length sequence of values of possibly different types. A tuple consists of zero or more values separated by commas, and often enclosed within parentheses, for example (from the tutorial):
>>> t = 12345, 54321, "hello!" >>> t[0] 12345 >>> t (12345, 54321, "hello!") >>> # Tuples may be nested: ... u = t, (1, 2, 3, 4, 5) >>> u ((12345, 54321, "hello!"), (1, 2, 3, 4, 5))
The empty tuple is written () and
a singleton tuple is written with a trailing comma, e.g.,
(1,), to distinguish it from a parenthesised expression.
See Section 5.3 of the tutorial for more information about tuples.
Sets. See Section 5.4 of the tutorial for information about sets.
Dictionaries. These are very important. A dictionary is a finite mapping from a set of distinct keys to a set of values. The keys are normally integers or characters or strings (or tuples of these). The values may be of any type. For example (from the tutorial):
>>> tel = {"jack": 4098, "fred": 4139}
>>> tel["guido"] = 4127 # adds a key-value pair
>>> tel
{"fred": 4139, "guido": 4127, "jack": 4098} # arbitrary order
>>> tel["jack"] # gets the values for a key
4098
>>> del tel["fred"] # deletes a key-value pair
>>> tel["irving"] = 4001
>>> tel
{"guido": 4127, "irving": 4001, "jack": 4098}
>>> tel.keys() # returns the list of keys
["guido", "irving", "jack"]
>>> tel.has_key("guido") # tests whether a key exists
True
>>> "guido" in tel # ditto
True
>>> tel.items() # returns the list of (key,value) pairs
[("guido",4127), ("irving",4001), ("jack",4098)]
To iterate over a dictionary, use a for-statement:
>>> for name, number in tel: ... print name, number
There are many functions for operating on dictionaries, e.g.,
len(d), d.values(), d.iteritems(). Try them
out.
See Section 5.5 of the tutorial for more information about dictionaries.
Classes. (From the tutorial:) A simple class definition:
class MyClass:
"""A simple example class""" # documentation comment
i = 12345 # initialises a (class) attribute
instances = [] # ditto
def f(self): # method of zero arguments
return "hello world"
To create an instance of this class:
x = MyClass()
(Note that you don't need a new operator as you do in Java.)
To define a constructor called whenever an instance is created, include in the class definition:
def __init__(self):
self.data = [] # initialises an (instance) attribute
instances.append(self) # adds the new instance to the class attribute
Such initialisers may have arguments in the normal way.
Attributes may be created after an instance has been created, e.g.,
x = MyClass() x.j = 123.0 # initialises a new (instance) attribute
Class attributes are accessed in the normal way:
>>> MyClass.i 12345
Methods are accessed in the normal way:
x = MyClass() x.f() # returns "hello world"
Every class should contain a method __str__(self) or
__unicode__(self) that is used to print instances of the class,
e.g.,
def __str__(self):
return "<MyClass: %d>" % i
Classes many be derived from one or more superclasses:
class DerivedClassName(BaseClassName):
statement
...
Field and method names in the derived class override names in the base class in the normal way.
Python also supports a limited form of multiple inheritance but the rules are complex and the feature is perhaps best avoided at first.
See Section 9 of the tutorial for more information about classes.
See Section 6 of the tutorial for information about modules.
Python (like JavaScript) supports functional programming (cf. Haskell and Scheme).
In particular, functions are values, one can define anonymous functions
(using lambda forms), and the common, higher-order, list-processing
functions map(function, sequence), filter(function,
sequence) and reduce(function, sequence) are
provided in the standard library. E.g.,
>>> filter(lambda(n): n % 2 == 0, [1,2,3,4,5]) [2, 4]
See Section 5.1.3 of the tutorial for examples of the use of these functions.
The following programs will be discussed and demonstrated in lectures.
For programs to be readable and maintainable, it is essential to follow the Python style guides for coding and documentation. Here is a summary:
self as the name for the first argument of
each method.
Python provides good facilities for automated program testing. Details will be provided later.
Data modelling is the process of identifying and organising the data that an application operates on. There are many courses and texts on the subject of data modelling. Here is a very brief, informal summary of the data modelling process for a particular application, based loosely on the entity-relationship style of data modelling.
Having performed such an entity-relationship design, we can then implement this design either as a set of relational tables (e.g., if we are using PHP and MySQL) or as a set of object-oriented classes (e.g., if we are using Python and Django).
In either case, if a (binary) relationship is one-to-one, there is no need for a separate table or class to represent the relationship; it suffices to add an attribute to one of the two entities to refer to the other, e.g., a husband attribute in the wife entity or vice versa. If a (binary) relationship is one-to-many, again there is no need for a separate table or class to represent the relationship; just add an attribute to the entity on the "many" side to refer to the entity on the "one" side, e.g., a supervisor attribute in the employee entity. If a (binary) relationship is many-to-many, it is necessary to use a separate table or class to represent the relationship, e.g., when an employee may work in several departments. If a relationship is between three or more entities, it is again necessary to use a separate table or class to represent the relationship.
Read the last paragraph again, carefully.
Suppose we wish to model students (with ids, names and addresses), courses (with codes, titles and convenors) and enrolments (with students, courses, years, grades). Each student has a unique id; each course has a unique code; each student can only be enrolled once in each course in each year. In each year, a student may be enrolled in many courses, and each course may have many students enrolled in it.
Analysing this familiar situation, we can conclude that the natural entities are students and courses and the natural relationship is the (many-to-many) enrolment association between students and courses. The natural primary key of the entity student is id; the natural primary key of the entity course is code; and the natural primary key of the entity enrolment is the attribute set {student_id, course_code, year}.
The statements below are valid SQLite3 statements.
create table students (
id int primary key /* auto_increment */,
name varchar(80) not null,
address varchar(80)
);
create table courses (
code varchar(8) primary key,
title varchar(80) not null,
convenor varchar(80),
description text
);
create table enrolments (
student_id int not null references students(id),
course_code text not null references courses(code),
year int(4) not null,
grade varchar(12),
primary key (student_id, course_code, year)
);
To use these statements with MySQL, it is only necessary to change the
id column of table students as follows:
id int primary key auto_increment,
Attributes student_id and course_code in table
enrolments are called foreign keys as they refer to keys
in other tables. It is a run-time error to create an enrolment tuple that
refers to a student or a course that does not exist (referential integrity
constraint violation). Similarly, if a student (or course) is deleted,
every enrolment that refers to that student (or course) must also be
deleted.
It is a serious (but common) error to try and include a student's courses
in the table students or a course's students in the table
courses. In the relational model, attribute values must be simple
values (such as names), not complex values (such as sets of
names).
Suppose there were another table, staff, say, with primary key
id of type int to represent staff members (convenors and
teachers). Then the attribute convenor in table
courses would need to have type int and to be a foreign
key that referenced staff(id).
If we also wanted to represent the teachers of a course, and a course could have one or more teachers, and different staff members could teach the same course in different years, then we would need at least one other table to represent the many-to-many relationship that a given staff member teaches a given course in a given year.
Here we use Python syntax.
class Student:
instances = []
def __init__(self,id,name):
self.id = id
self.name = name
instances.append(self)
def __str__(self):
return "s%d: %s" % (self.id, self.name)
class Course:
def __init__(self,code,title):
self.code = code
self.title = title
def __str__(self):
return "%s: %s" % (self.code, self.title)
class Enrolment:
def __init__(self,student,course,year):
self.student = student # reference to student object
self.course = course # reference to course object
self.year = year
def __str__(self):
return "(s%d, %s, %d)" % (self.student.id, self.course.code, self.year)
To create instances of these classes, we could do the following:
>>> s = Student(2345678,"John")
>>> s.address = "Nathan"
>>> print s
s2345678: John
>>> c = Course("7401ICT", "eService Technology")
>>> c.convenor = "Rodney"
>>> e = Enrolment(s, c, 2010)
>>> e.grade = "HD"
>>> print e
(s2345678, 7401ICT, 2010)
>>> t = Student(7777777,"Betty")
>>> Student.instances
[s2345678: John, s7777777: Betty]
Again, if we used a separate class Staff for staff members, we
would have to make the attribute convenor in class
Course a reference to an instance of class Staff, and
write something like this:
>>> r = Staff("Rodney") # Create staff member instance
>>> et = Course("7401ICT", "eService Technology")
>>> et.convenor = r # reference to staff member object
Access to all instances of a class in Django does not require maintaining an explicit list of instances using class attributes as above, and will be described shortly.
Note that the class model is rather different in Python from Java, so you need to read the manual, or at least the tutorial, be careful, and avoid jumping to any conclusions.
See my Web Programming notes for a more detailed introduction to relational database design and the SQL query language. (When using Django, it is not normally necessary to use SQL at all.)