Introduction to Programming in Python

Why Python?

Python is a high level, general purpose language which offers a lot of flexibility in how you can use it. It is widely used and freely available and runs on many different platforms. Python is extensible. There are many modules available for solving all kinds of problems: from fetching data from web servers, writing games, doing large numerical computations, and so on. Python is interpreted. Although this is not an absolute benefit - for us, this means writing code and running is more closely coupled, which is great for exploring new problems and getting feedback from your code as you write it.

The typical usage of Python consists of typing commands or running programs in an interpreter. It can certainly be used to replace UNIX shell scripting languages in orchestrating batch computations. But it is really a full-fledged and well-developed object-oriented computer language and that is how it is typically used. One of its powerful features is the ability to easily import functionality present in libraries that are external to the main built-in functionality. Many libraries needed for numerical computing now have Python bindings (can be called from Python programs). Typically these are standard numerical libraries like LAPACK, ARPACK and many others that may have been written in FORTRAN or some other language. But these can be used from within Python programs seamlessly. There is also a very powerful and efficient library for handling arrays, which is essesntial for numerical computing. Due perhaps to all these reasons, Python has become a favorite (along with MATLAB) for numerical computing. There is also a powerful standard library that is part of every Python distribution.

The learning curve for Python is not as steep as some other popular languages like C++ or Java which makes it especially suited for someone with little or no programming experience. It is also rich and powerful (and mostly efficient when used appropriately) so that it can also satisfy the needs and expectations of expert programmers.

We will use version 2.7 rather than version 3.x. Pyhton 2.7 is still well-maintained and being actively developed.

Python programs can also be byte compiled. The typical workflow when using Python is more like MATLAB than like C/C++. For example, to write a C/C++ program you have to compile and then will likely do a load/link phase. In Python you just write a program and run it.

There are many integrated development environments available. And most UNIX systems (including Mac OS X) are factory-shipped with Python. Typing python at the UNIX prompt in a terminal in Mac OS X starts up a Python interpreter. But will use a popular interpreter called IPython. The benfits of using IPython over the standard interpreter will become clearer as we proceed.

The IPython interpreter can be started by typing ipython at the UNIX prompt.

Basic Expressions

We'll start out by having Python say hello to us. To do this, we'll write

In [1]:
print "Hello World"
Hello World

Great! That was easy and things are looking good!

Now let's doing some number crunching, using Python as our calculator

In [2]:
1032 + 4334
Out[2]:
5366
In [3]:
(1232 + 3.0) * 43
Out[3]:
53105.0
In [4]:
2.0 * (1032.0 + 3.0) / (3.0 + 4.0*7.1)
Out[4]:
65.92356687898089
In [6]:
19 % 7
Out[6]:
5

If we want to compare two numbers, we can just write (almost) what we'd expect

In [8]:
2**6 >= 60
Out[8]:
True
In [7]:
346 % 2 == 0
Out[7]:
True

This is a good start, but quickly becomes difficult to manage. Let's start with the simplest abstraction and start naming things.

In [9]:
P = 800.0
r = 0.01
t = 2.0
print P*(1 + r)**t
816.08
  • Upper and lowercase letters, underscore, and numbers
  • Must start with a letter or underscore
  • No spaces allowed
  • Keywords (a.k.a. reserved words) can not be used as variable names, e.g., for, while, if, def, import, True, False
  • We will see more such reserved words as we learn more Python syntax

You can work with expressions involving other data types. For example, strings are a very common data type used to represent a sequence of characters:

In [4]:
s = "The String Called s!"
t = "The String Called t!"
print s
print s + " " + t
print len(s)
The String Called s!
The String Called s! The String Called t!
20

Or, boolean data, which are used to represent True / False values. This data type supports all the standard logic predicates.

In [36]:
x = True
y = False
print x
print x and y
print x or y
print not x
print not not x
True
False
True
False
True

You can also do things like reassign a variable in order to increment it.

In [11]:
x = 10
x = x + 1
print x
11

Or if you prefer, you can use the short-hand for it:

In [6]:
x = 10
x += 1
print x
11

Many of the standard operators (+, -, *, /, ...) support this kind of short-hand notation. You're free to use whichever you like, though.

Exercise 1

  1. Translate the following expression into code: $$\frac{1.3/1.2 + 4.3 \times 2.0}{1.5 - 2.3}$$ and print the result.

  2. Check if 4431 and 7523 are equal mod 7.

  3. Define pi using the expression 355/113. Use it to compute the circumference and area of a circle of radius 5. (There's a subtle error which can occur in this problem! Try printing out pi after defining it. Do you get an unexpected answer? What do you think happened?)

Program Flow

Now, suppose you want to do something like compute the minimum of two numbers or compute the absolute value of two numbers. How would you do something like this? Well, we know one definition for these is:

$$ \min(a, b) = \begin{cases} a & \mbox{if } a < b \\ b & \mbox{if } a \ge b \end{cases} $$$$ | a | = \begin{cases} a & \mbox{if } a > 0 \\ -a & \mbox{if } a < 0 \\ 0 & \mbox{if } a = 0 \end{cases} $$

How would we turn this into code? Let's start with just the decision making itself. We can do this by introducing conditional statements:

In [12]:
x = 30
y = 20

if x < y:
    print x
else:
    print y
20

If there are multiple or even many choices, then we can write something like:

In [1]:
day = 5

if day == 0:
    print "Monday"
elif day == 1:
    print "Tuesday"
elif day == 2:
    print "Wednesday"
elif day == 3:
    print "Thursday"
elif day == 4:
    print "Friday"
elif day == 5:
    print "Saturday"
elif day == 6:
    print "Sunday"
else:
    print "...that's not even a real day..."
Saturday

Unfortunately, this is only a part of what we asked for above. What we really want is some function that we can plug values into and get a result. So, now we'll introduce a powerful new abstraction: defining functions. Let's see how we'd turn our $\min$ function into an actual computation.

In [2]:
def min(x, y):
    if x < y:
        return x
    else:
        return y

print min(3, 5)
print min(9, 4)
print min(min(5, 2), 3) # Can even plug a result into another computation!
3
4
2

One of the most important consequences of this is approach us, we don't care how a function is implemented (assuming it's implemented correctly and efficiently).

For example, if someone decides to implement a function differently it shouldn't affect the way we use the function!

In [3]:
# One implementation of double
def double(x):
    return 2 * x

print double(7)

# Another implementation of double
def double(x):
    return x + x

print double(7)
14
14

As you may have noticed, we can have functions of whatever number of arguments as we like. For example:

In [5]:
def add_three(x, y, z):
    return x + y + z

print add_three(4,6,5)
15

Or even a kind of silly function like:

In [6]:
def the_number_three():
    return 3

print the_number_three()
3

One thing which may trouble you is having to remember the order of the arguments a function takes. Python provides a nice syntax sometimes refered to as "named arguments". An example of this is:

In [7]:
def show_a_minus_b(a, b):
    return a - b

print show_a_minus_b(a=2, b=1)
print show_a_minus_b(b=7, a=2)
print show_a_minus_b(2, 7)
1
-5
-5

This will be useful later on, as a number of functions use this to supply optional arguments to change their behavior.

Exercise 2

  1. Write a short function which decides if an integer is even.

  2. Write an absolute value function.

  3. Write a function which squares a number and then use it to write a function which takes three integers and returns the sum of their squares.

  4. Write a function which computes the factorial of a nonnegative integer.

Importing functionality

Python has lot of useful built-in functionality. But that is not enough to solve computational mathematics problems in science and engineering. One nice feature of Python is how easily it allows you to extend the built-in functionality by importing from modules and packages. There are many variations on how to import which are explored below. The first box below imports the functions sin and exp and the constant pi from the module called math.

Importing from modules and packages

In [7]:
from math import sqrt, pi, exp

Now you can use these functions and constant in your program by referring to them as sqrt, pi, and exp.

Using imported functionality

In [8]:
print sqrt(2)
print pi
print exp(1)
1.41421356237
3.14159265359
2.71828182846

This form of importing requires that you list every object that you plan to use. If you plan to use everything in a module you could import every thing as follows:

Importing everything (not my preference usually)

In [9]:
from math import *

print sqrt(2)
print pi
print exp(1)
print log10(1000)
1.41421356237
3.14159265359
2.71828182846
3.0

I generally don't like to do import * because there can be name clashes. One compromise between these two methods is to import the module name and use it to refer to the needed objects without having to import them.

Importing module or package name

In [10]:
import math

print math.sqrt(2)
print math.pi
print math.exp(1)
print math.log10(1000)
1.41421356237
3.14159265359
2.71828182846
3.0

You can also give the module any name you want and then use that to refer to it in your code.

Renaming imported module or package

In [11]:
import math as m

print m.sqrt(2)
print m.pi
print m.exp(1)
print m.log10(1000)
1.41421356237
3.14159265359
2.71828182846
3.0

How to Repeat Things

In some problems, we may like or prefer to have Python repeat a procedure over and over again until we're happy with the result. For example, let's say you want to print out the squares of the numbers 1 to 10.

In [1]:
n = 1

while n <= 10:
    print n*n
    n += 1 # Short hand for x = x + 1
1
4
9
16
25
36
49
64
81
100

Similarly, you may try to add the first 100 integers.

In [21]:
n = 1
total = 0

while n <= 100:
    total += n
    n += 1

print total
5050

Another very useful, more general way of doing these kinds of things is by using iterators. One of the simplest iterators abstracts away the process of looping over a range of numbers.

We can rewrite the computations above using iterators as follows:

In [1]:
for n in range(1, 11): # [1, 11) <---
    print n*n
1
4
9
16
25
36
49
64
81
100
In [2]:
total = 0

for n in range(1, 101):
    total += n

print total
5050

Every now and then, you want a little more control over when to leave a loop. In Python you can use the break statement to do this.

In [4]:
n = 0

print 'start!'

while True:
    print 'before!'
    if n == 3:
        break
    print 'after!'
    n += 1

print 'done!'
start!
before!
after!
before!
after!
before!
after!
before!
done!

Exercise 3

  1. Write a function which adds up the squares of the first $n$ odd integers.

  2. Write a function which prints the natural numbers less than $n$ which are divisible by 3 and divisible by 5 but not divisible by 10.

  3. Write a program which prints out an 16 x 16 "chess board" which indicates a black square with a 'B' and a white square with a 'W'. Remember that in a chessboard, the right corner of the first row for each player is a white square.

  4. Print out the first ten Fibonacci numbers.

  5. If you used recursion to implement the factorial function earlier, use either a while loop or an iterator to rewrite the factorial function.

Rudimentary Testing

As you develop small pieces of code, you'll want to check if they are correct. For very simple code this isn't usually a problem. But, even in those cases, there can be a danger of "one-off errors" or not predicating a strange type error. In those cases, it can be useful to write some code to test the functions you're writing. One of the simplest ways to do this is by using the assert command.

In [20]:
def sum_first_n(n):
    k = 0
    s = 0
    while k <= n:
        s += k
        k += 1
    return s
In [21]:
def test_it():
    assert sum_first_n(0) == 0
    assert sum_first_n(1) == 1
    assert sum_first_n(2) == 3

test_it()

Again, this is only a rudimentary method of testing code. There's a whole industry dedicated to various testing methods, all of which is beyond the scope of this short bootcamp! Still, it's good to be aware of these things.

Additional Remarks

Documentation and Commenting

Commenting code can be done using the # symbol:

In [2]:
# assigning my variables
x = 4
y = 6

# checking which is smaller
x < y
Out[2]:
True

This can be helpful, but be careful not to be overzealous on comments. If you choose good variables names and break-up code into clear procedures, lots of comments are often not required.

In [3]:
min(4, 6)  # speaks for itself, so comment shouldn't be here.
Out[3]:
4

Printing in Different Ways

In the chessboard problem you will need to know how to print without creating a new line and how to print a blank line. This can be done as follows:

In [4]:
# these don't print newlines!
print 'W',
print 'B',

# a blank print will generate a line break
print
W B

Multi-Assignment

One last nice syntactic feature is doing multi-assignment:

In [2]:
a, b = 1, 1

for n in range(10):
    a, b = b, a+b  # multi-assignment

This allows us to leverage Python to get around a common alternative pattern of introducing a temporary variable:

In [8]:
a, b = 1, 1

for n in range(10):
    t = a+b
    a = b
    b = t