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.

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

In [1]:

```
print "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]:

In [3]:

```
(1232 + 3.0) * 43
```

Out[3]:

In [4]:

```
2.0 * (1032.0 + 3.0) / (3.0 + 4.0*7.1)
```

Out[4]:

In [6]:

```
19 % 7
```

Out[6]:

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

In [8]:

```
2**6 >= 60
```

Out[8]:

In [7]:

```
346 % 2 == 0
```

Out[7]:

In [9]:

```
P = 800.0
r = 0.01
t = 2.0
print P*(1 + r)**t
```

- 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

In [4]:

```
s = "The String Called s!"
t = "The String Called t!"
print s
print s + " " + t
print len(s)
```

In [36]:

```
x = True
y = False
print x
print x and y
print x or y
print not x
print not not x
```

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

In [11]:

```
x = 10
x = x + 1
print x
```

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

In [6]:

```
x = 10
x += 1
print x
```

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

Check if 4431 and 7523 are equal mod 7.

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?)

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
```

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

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!
```

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)
```

In [5]:

```
def add_three(x, y, z):
return x + y + z
print add_three(4,6,5)
```

Or even a kind of silly function like:

In [6]:

```
def the_number_three():
return 3
print the_number_three()
```

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)
```

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

Write an absolute value function.

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.

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

*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`

.

In [7]:

```
from math import sqrt, pi, exp
```

In [8]:

```
print sqrt(2)
print pi
print exp(1)
```

In [9]:

```
from math import *
print sqrt(2)
print pi
print exp(1)
print log10(1000)
```

`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.

In [10]:

```
import math
print math.sqrt(2)
print math.pi
print math.exp(1)
print math.log10(1000)
```

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

In [11]:

```
import math as m
print m.sqrt(2)
print m.pi
print m.exp(1)
print m.log10(1000)
```

In [1]:

```
n = 1
while n <= 10:
print n*n
n += 1 # Short hand for x = x + 1
```

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
```

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
```

In [2]:

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

*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!'
```

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

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.

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.

Print out the first ten Fibonacci numbers.

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

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()
```

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]:

In [3]:

```
min(4, 6) # speaks for itself, so comment shouldn't be here.
```

Out[3]:

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
```

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
```

In [8]:

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