# Programming in Python

## Why Python?

Python is expressive! It's a high level, general purpose language which offers a lot of flexibility in how you can use it.

Python is availible! It's a widely used, free language that runs on many different platforms.

Python is extensible! There are many modules are availible for solving all kinds of problems from fetching data from web servers, writing games, doing large numerical computations, and so on...

Python is interpreted! Though 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.

## 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 + 431
Out[2]:
1463
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 [5]:
1102 % 7
Out[5]:
3

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

In [6]:
2 ** 8 >= 200
Out[6]:
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 [8]:
P = 800.0
r = 0.01
t = 2.0
print P*(1 + r)**t
816.08

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

In [35]:
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

I 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 I prefer, I can use the short-hand for it:

In [37]:
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}$

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

3. Define pi using the approximation $\frac{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 I want to do something like compute the minimum of two numbers or compute the absolute value of two numbers. How would I 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 > 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 [13]:
day = 4

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..."
Friday

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

In [14]:
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) # I can even plug a result into another computation!
3
4
2

One of the most important consequences of this is approach us, I don't care how a function is implemented. (Assuming it's implemented correctly and maybe whether it's efficient or not! But, most importantly, I don't have to worry about correctness!)

For example, if one of my friends comes along and decides to implements a function slightly (or not so slightly) differently it shouldn't affect the way I use the function!

In [30]:
# my implementation of double
def double(x):
return 2 * x

print double(7)

# my friend's 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 [31]:
def add_three(x, y, z):
return x + y + z

Or even a kind of silly function like:

In [33]:
def really_random_number():
return 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 [34]:
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)
1
-5

This will be useful later on, as a number of functions use this to supply optional arguments to change their behavior. Though, for now, I'm only mentioning it.

## Exercise 2

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

2. Fill the details for the 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 an integer.

# Importing Functionality from Modules

As you may imagine, we don't always have to do all the hard work and write everything ourselves. In fact, a few of the functions we've define such as min and abs are built in to Python. So, how can we use an externally defined function or definition? Let's add some new functions to our "calculator" from earlier from Python's math module.

In [18]:
from math import sin, cos, pi
print pi
print sin(2.1*pi)
3.14159265359
0.309016994375

This is great when you just need a few things from a module. You can also import the entire module as a namespace.

In [19]:
import math
print math.pi
print math.sin(1.0)
3.14159265359
0.841470984808

Now where did this come from? How would you know where to look? The easiest place to see what's availible is by searching "Python standard library". For example, you'll find the math module listed there. If you take a look you'll see that there's a lot there!

## 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 I want to print out the squares of the numbers 1 to 10.

In [20]:
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, I 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, 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'.

4. Print out the first ten Fibonacci numbers.

5. Try reimplementing the factorial function using either a while loop or an iterator. Which do you think is better? Which do you think is easier to understand?

## 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 [17]:
def sum_first_n(n):
k = 0
s = 0
while k <= n:
s += k
k += 1
return s
In [18]:
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 workshop! Still, it's good to be aware of these things.

## Additional Remarks

### Documentation and Commenting

One question which came up was how to document or comment code. This 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

Another question which came up regarding the chessboard problem was 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 [6]:
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