Python in 50 minutes

For more details, see the Python tutorial.

A simple program

# welcome.py
print "Welcome to Sage Days!"

$ python welcome.py
Welcome to Sage Days!

Python code is compiled into byte code, which is executed in a Python virtual machine. But you may never know it unless you look.

A computation

# computation.py
a = 1 + 2 ** 3
print a

$ python computation.py
9

Arithmetic operators:

+ addition
- subtraction
* multiplication
/ division
** exponentiation
% modulus

Everything in Python is an "object" and has a type (e.g. int, float, string, etc). However, an object's type is a property of the object alone and not of a variable that points to it. This is called dynamic typing. It's a real convenience but occassionally requires a bit of thought.

# computation.py
a = 1 + 2 ** 3
print a
a = "Now a is a string"
print a

$ python computation.py
9
Now a is a string

Anything in a line following the character "#" is ignored by Python.

Data Types

integers
long integers
floats (like doubles in C or Java)
strings
lists
dictionaries
files

The Python interpreter

>>> a = 5
>>> b = 3
>>> a*b
15
>>> a%b
2
>>> a/b
1

Suppose we really want 5/3 = 1.666...

>>> a * 1.0 /b
1.6666666666666667

or

>>> float(a)/b
1.6666666666666667

There are operators that convert between types if you need them. A useful one is str(a), which produces the string representation of the object a.

>>> a = 5
>>> a = 5.01
>>> a
5.0099999999999998
>>> str(a)
'5.01'

Python will sometimes convert between types for us. For instance,

>>> 10**100
10000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000L

produces a long integer. But sometimes Python needs some help.

>>> a = 4
>>> "the value of a is " + a
Traceback (most recent call last):
  File "", line 1, in ?
TypeError: cannot concatenate 'str' and 'int' objects
>>> "the value of a is " + str(a)
'the value of a is 4'

Strings

Strings may be defined with either ' ' or " ".

>>> a = "hello"
>>> print a
hello
>>> a = '$\alpha$'
>>> print a
$lpha$
>>> a = r'$\alpha$'
>>> print a
$\alpha$

There are lots of operators for dealing with strings. How do we find them?

>>> dir(a)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__',
'__eq__', '__ge__', '__getattribute__', '__getitem__',
'__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__',
'__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__',
'__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count',
'decode', 'encode', 'endswith', 'expandtabs', 'find', 'index',
'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle',
'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind',
'rindex', 'rjust', 'rsplit', 'rstrip', 'split', 'splitlines',
'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper',
'zfill']

>>> help(a.replace)

Help on built-in function replace:

replace(...)
    S.replace (old, new[, count]) -> string

    Return a copy of string S with all occurrences of substring
    old replaced by new.  If the optional argument count is
    given, only the first count occurrences are replaced.

>>> b = a.replace('alph', 'bet')
>>> print b
$\beta$

Strings are just arrays of characters. This will be useful when dealing with lists too.

>>> a = r'$\alpha$'
>>> a[2:5]
'alp'
>>> a[:4]
'$\\al'
>>> a[2:]
'alpha$'
>>> a[-1]
'$'
>>> a[::2]
'$apa'

Strings are immutable; that is, they may not be modified.

>>> a[2] = 'b'
Traceback (most recent call last):
  File "", line 1, in ?
TypeError: object does not support item assignment

Lists

Lists are arrays, like strings, but they can hold arbitrary data types and they may be modified

>>> a = [10, 'sage', 3.14159]
>>> a
[10, 'sage', 3.1415899999999999]

or

>>> a = [ ]
>>> a.append(10)
>>> a.append("sage")
>>> a.append(3.14159)
>>> a
[10, 'sage', 3.1415899999999999]

or

>>> a = [0]*10
>>> a
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__delslice__', '__doc__', '__eq__', '__ge__', '__getattribute__',
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__',
'__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__',
'__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__',
'__setslice__', '__str__', 'append', 'count', 'extend', 'index',
'insert', 'pop', 'remove', 'reverse', 'sort']

Unlike strings, lists are mutable:

>>> a = [10, 'sage', 3.14159]
>>> a[0] = 11
>>> a
[11, 'sage', 3.1415899999999999]

Watch this:

>>> b = a
>>> b.pop()
3.1415899999999999
>>> a
[10, 'sage']

Variables are references to objects. When we say b = a, we simply create a new reference to the same object that a references. When we modify that object through the reference b, the changes appear under other references as well.

If this is not the behavior we want, we need to make a copy of the object.

>>> a = [10, 'sage', 3.14159]
>>> b = a[:]
>>> b.pop()
3.1415899999999999
>>> a
[10, 'sage', 3.1415899999999999]

Dictionaries

Dictionaries provide a means of storing objects by key rather than relative position.

>>> a = { 'zero': [0, 0], 'e1': [1, 0], 'e2': [0, 1] }
>>> a
{'zero': [0, 0], 'e1': [1, 0], 'e2': [0, 1]}
>>> a['zero']
[0, 0]

or

>>> a = { }
>>> a['zero'] = [0, 0]
>>> a['e1']   = [1, 0]
>>> a['e2']   = [0, 1]
>>> a
{'zero': [0, 0], 'e1': [1, 0], 'e2': [0, 1]}
>>> dir(a)
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__',
>>> '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__',
>>> '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__',
>>> '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
>>> '__repr__', '__setattr__', '__setitem__', '__str__', 'clear',
>>> 'copy', 'fromkeys', 'get', 'has_key', 'items', 'iteritems',
>>> 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem', 'setdefault',
>>> 'update', 'values']

>>> a.has_key('zero')
True
>>> a.has_key('e3')
False

Valid keys are objects that are hashable. (Unfortunately, lists are not hashable.)

Conditionals

>>> a = -2
>>> if a < 0:
...     print 'a < 0'
... elif a > 0:
...     print 'a > 0'
... else:
...     print 'a = 0'
...
a < 0

Notice the syntax. Blocks of code are denoted by identation rather than { ... }. A colon ends a statement before a block of code. Also, we do not need parentheses around the logical expression.

>>> a = [3, 5, 7, 11, 13]
>>> if 2 not in a and len(a) < 10:
...     a.insert(0, 2)
...
>>> a
[2, 3, 5, 7, 11, 13]

The values of booleans are True or False.

Loops

>>> for x in a:
...     print x
...
2
3
5
7
11
13

>>> a = 3.2
>>> while a > 1:
...     a /= 2
...
>>> str(a)
'0.8'

break causes us to leave a loop immediately.

>>> a = [3, 5, 7, 11, 13]
>>> for x in a:
...     if x == 7:
...             print 'list contains 7'
...             break
...
list contains 7

In the same way, continue causes us to go back to the top of the loop immediately.

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for i in range(10):
...     print i*i
...
0
1
4
9
16
25
36
49
64
81

>>> for i in range(-5, 6, 2):
...     print i
...
-5
-3
-1
1
3
5

range generates the list before running the loop. xrange is similar to range except that elements in the list are generated as needed.

>>> sum = 0
>>> for x in xrange(1, 1000001):
...     sum += x
...
>>> sum
500000500000

List comprehensions give a convenient way to create lists

>>> squares = [ x*x for x in range(0, 11) ]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Working with files

>>> output = open('out', 'w')
>>> output.write('hello\n')
>>> output.write('there\n')
>>> output.close()
>>>
>>> input = open('out')
>>> lines = input.readlines()
>>> lines[0]
'hello\n'
>>> lines[1]
'there\n'
>>> input.close()

Functions

We just added together the first million positive integers. We can turn this into a function so that we may vary the number of terms to add.

>>> def sum(n):
...     s = 0
...     for x in xrange(1, n+1):
...             s += x
...     return s
...
>>> sum(1000000)
500000500000
>>> sum(35)
630

Notice that we do not need to specify the type of the object returned by the function.

Suppose we want to allow our function to add the integers between a and n.

# sum.py
# Allow 	sum(n) to add [1, 2, 3, ... n]
#	or	sum(a, n) to add [a, a+1, ..., n]
def sum(*args):
    if len(args) == 1:
        start = 1
        stop = args[0]
    else:
        start = args[0]
        stop = args[1]
    s = 0
    for x in xrange(start, stop+1):
        s += x
    return s

print sum(1000000)
print sum(3, 4)

$ python sum.py
500000500000
7

Functions may have no arguments. In this case, you must put () after the function's name when calling it.

>>> def f():
...     print 'hi'
...
>>> f

>>> f()
hi
>>>    

Scope of variables

Variables inside a block of code are only visible inside that block.

# scope0.py
def f(n):
    x = 3
    return x**n

print f(4)
print x

$ python scope0.py
81
Traceback (most recent call last):
  File "scope0.py", line 6, in ?
    print x
NameError: name 'x' is not defined

# scope1.py
x = 3
def f(n):
    return x**n

print f(4)
print x

$ python scope1.py
81
3

# scope2.py
x = 3
def f(n):
    x += 1
    return x**n

print f(4)
print x

$ python scope2.py
Traceback (most recent call last):
  File "scope2.py", line 6, in ?
    print f(4)
  File "scope2.py", line 3, in f
    x += 1
UnboundLocalError: local variable 'x' referenced before assignment

# scope3.py
x = 3
def f(n):
    global x
    x += 1
    return x**n

print f(4)
print x

$ python scope3.py
256
4

Classes

Python is an object-oriented language so we may create our own data types, which will hold data and allow us to access and manipulate the data. This is a good way to get to know your self.

# Circle.py
import math

class Circle:
    def __init__(self, center, radius):
        self.center = center
        self.radius = radius
    def area(self):
        return math.pi * self.radius**2
    def contains(self, point):
        dx = point[0] - self.center[0]
        dy = point[1] - self.center[1]
        return math.sqrt(dx*dx + dy*dy) < self.radius

>>> c = Circle([3,4], 5)
>>> c.area()
78.539816339744831
>>> c.contains([1,1])
True
>>> c.contains([-1,1])
False
>>> c.center
[3, 4]
>>> c.center = [2,3]    # don't do this!  

In general, it is better to access variables inside a class using functions provided by the class.

# Circle.py
import math

class Circle:
    def __init__(self, center, radius):
        self.center = center
        self.radius = radius
    def area(self):
        return math.pi * self.radius**2
    def contains(self, point):
        dx = point[0] - self.center[0]
        dy = point[1] - self.center[1]
        return math.sqrt(dx*dx + dy*dy) < self.radius
    def getradius(self):
        return self.radius
    def getcenter(self):
        return self.center
    def setradius(self, r):
        self.radius = r
    def setcenter(self, c):
        self.center = c

Modules

# Geometry.py
import math

class Circle:
    def __init__(self, center, radius):
        self.center = center
        self.radius = radius
    def area(self):
        return math.pi * self.radius**2
    def contains(self, point):
        dx = point[0] - self.center[0]
        dy = point[1] - self.center[1]
        return math.sqrt(dx*dx + dy*dy) < self.radius

class Square:
    def __init__(self, ll, side):
        self.lowerleft = ll
        self.side = side

    def area(self):
        return self.side * self.side
    def contains(self, point):
        dx = point[0] - self.lowerleft[0]
        dy = point[1] - self.lowerleft[1]
        if dx < 0 or dx > side:
            return False
        if dy < 0 or dy > side:
            return False
    
def unitcircle():
    return Circle([0, 0], 1)

def unitsquare():
    return Square([0, 0], 1)

Using the module

# geometrytest.py
import Geometry

square = Geometry.unitsquare()
circle = Geometry.unitcircle()
print square.area(), circle.area()

$ python geometrytest.py
1 3.14159265359

or

# geometrytest.py
from Geometry import *

square = unitsquare()
circle = unitcircle()
print square.area(), circle.area()

$ python geometrytest.py
1 3.14159265359