Jeremy Sanders
October 2011
You can also simply call Fortran, C or C++ programs for it, so that you can write time critical bits of a program in one of these languages and the rest in Python. It also has a lot of useful libraries (see Section 8) to handle numerics (numpy) and scientific calculations (e.g. integration in scipy), FITS files (pyfits). There is even a version of IRAF supporting Python (pyraf).
This is a brief introduction to Python, but there are books in the library and a lot of documentation online. Start looking at http://www.python.org/doc/ .
jss@xport10:~/text> /usr/local/bin/python Python 2.4.3 (#2, Oct 6 2006, 07:52:30) [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> 10+10 20 >>> 'hello '+'there' 'hello there' >>> a=10 >>> b=20 >>> a+b # the value is printed at the prompt 30 >>> print a+b # you need to print to print inside a script 30 >>> import math >>> print math.sin(math.pi/4.0) 0.707106781187 >>> help(math.sin) # gets help on a function from documentationPress ctrl+d to exit. ipython gives you a nicer environment than the standard python prompt, and allows you to run unix commands.
#!/usr/local/bin/python # this is a comment... print "What is your name?" name=raw_input() print "Hello,", name
To make it executable type chmod +x test.py, then run it by typing its name, test.py on the unix prompt. Scripts can be placed in /home/username/bin/ to get added to the path to become commands that can be used anywhere.
>>> 0 # integer 0 >>> 0. # float 0.0 >>> 1+2 # add integers 3 >>> 3.14*200 # multiply floats 628.0 >>> a=3.5 >>> a**2 # square 12.25 >>> b=(a**2)/4 >>> b 3.0625 >>> 10 % 3 # remainder of 10/3 (integers only) 1 >>> 10 / 3 # dividing integers by integers gives whole numbers 3 >>> 10. / 3 # dividing integer or float by float gives floats 3.3333333333333335 >>> 3e4 / 1000 # scientific notation 1.234e-10 can be used for floats 30.0 >>> int(3.141592) # convert float to int 3 >>> float(3) # convert int to float 3.0 >>> str(3) # convert int to string '3'
>>> a='hello there'
>>> b='fred'
>>> a + ' ' + b
'hello there fred'
>>> a[1:4] # characters count from 0, and end is +1
'ell'
>>> a[1:] # select string from character 1 to end
'ello there'
>>> a[:4] # select characters 0 through 3
'hell'
>>> a[:-1] # negative indices count from end
'hello ther'
>>> len(b) # string length
4
>>> a="double quotes also work"
>>> a="you can embed\nnew lines"
>>> print a
you can embed
new lines
>>> a = ' lots of space '
>>> b = a.strip() # remove spaces from start and end
>>> print b
'lots of space'
>>> a.split() # break a string into a list
['lots', 'of', 'space']
>>> 'hi, there, fred'.split(',') # split on commas
['hi', ' there', ' fred']
>>> str(4) # convert number to string
'4'
>>> a = """This is a
multi line string. Which
can go over many lines, and include new
lines naturally"""
>>> del a # delete variable (forget it)
Strings have lots of sophisticated methods (like split) which can find
substrings, replace substrings, or change case:
>>> a="This is a test"
>>> a.find('test') # get starting character of substring
10
>>> a[:a.find('test')] # slice according to character
'this is a '
>>> 'SHOUTING'.lower() # convert to lowercase
'shouting'
>>> a.replace('test', "fish") # replace substring
'This is a fish'
One useful feature is the formatting operator, % which formats
numbers and strings
>>> '%s counts to %i sleep %.3f but %e' % ('fred', 10, 4.1, 3)
'fred counts to 10 sleep 4.100 but 3.000000e+00'
>>> a=[1,2,3,'cow']
>>> a[1] # index list
2
>>> a[2:4] # slice list
[3, 'cow']
>>> a[2] = 'moo' # assign element
>>> a
[1, 2, 'moo', 'cow']
>>> len(a) # length of a
4
>>> a.append('steak') # add to list
>>> a
[1, 2, 'moo', 'cow', 'steak']
>>> a.insert(1, 1.5) # insert into list
>>> a
[1, 1.5, 2, 'moo', 'cow', 'steak']
>>> a.append([1,2,3]) # lists can contain lists
>>> a
[1, 1.5, 2, 'moo', 'cow', 'steak', [1, 2, 3]]
>>> a.index('moo') # find item in list
3
>>> a[-1][1] # index item in list in list
2
>>> a = [] # a blank list
>>> a.append('foo')
>>> a
['foo']
>>> range(5) # creates a list filled with numbers
[0, 1, 2, 3, 4]
>>> a = [5, 3, 1, 0, -1, 6]
>>> a.sort() # sort list in-place
>>> a
[-1, 0, 1, 3, 5, 6]
>>> del a[0:2] # delete first two elements
>>> a=(0,1,2,3) # make tuple
>>> a[-4:-1] # slice tuple
(0, 1, 2)
>>> a[-3:-1] # slice again
(1, 2)
>>> a[1]='fred' # this doesn't work!
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object does not support item assignment
>>> list(a) # convert tuple -> list
[0, 1, 2, 3]
>>> tuple(['hi', 'ho']) # convert list -> tuple
('hi', 'ho')
One-length tuples are a special case as otherwise they would be
confused with brackets.
>>> a = (1,) # a one-length tuple >>> a = () # a zero-length tuple
>>> a = {} # empty dictionary
>>> a[3] = 10 # map key 3 to value 10
>>> a[4] = 'fred' # map key 4 to value 'fred'
>>> a[3] # prints out value mapped to 3
10
>>> a['amazing'] = 'wibbles'
>>> a['fred'] = 3.141592
>>> a['amazing']
'wibbles'
>>> a['amazing'] = 'spam'
>>> 'fred' in a # test for existence
True
>>> a = {'fred': 10, 'tim': 15, 'keith': -1}
>>> a['tim'] = a['fred'] + 42 # reassign value
>>> a
{'tim': 52, 'keith': -1, 'fred': 10}
>>> a.keys() # return list of keys
['tim', 'keith', 'fred']
>>> a.values() # return list of values
[52, -1, 10]
>>> a.items() # return tuple pairs of key,value
[('tim', 52), ('keith', -1), ('fred', 10)]
>>> a[(1,2,3)] = -1 # tuples can act as keys (e.g. grid)
>>> a[(1,2,3)]
-1
>>> f = open('fred.dat') # open file for reading
>>> print f.readline() # prints a (newline \n is also returned)
a
or
>>> s=f.read() # would read entire file and put into s
or
>>> lines = f.readlines() # reads each line and construct a list
>>> print lines
['a\n', 'b\n', ....]
>>> f.close() # close file
You can also write to files:
>>> f = open('out.dat', 'w') # open for writing mode
>>> print >>f, "a is", 42 # print to file
>>> f.write('This is a line\n') # write a string
>>> f.close()
Mode 'a' can append to a file (add to it).
>>> a = [1,2,3,4] # make list object >>> b = a # makes b point to same list object >>> b.append(5) # add item to list object >>> a # a and b point to same object [1, 2, 3, 4, 5] >>> b [1, 2, 3, 4, 5] >>> b = list(a) # make b a new list based on a >>> b.append(6) >>> a # a is not modified [1, 2, 3, 4, 5]
Really the command a = b assigns a new name ``a'' for the object ``b''. This allows you to do this
def func(x): x.append(5) a = [1,2,3] func(a) print a # outputs [1, 2, 3, 5]
Note that immutable objects (like numbers, strings or tuples) do not have this property.
>>> a = 10 # makes a point to object 10 >>> b = a # makes b point to object 10 >>> a = 11 # makes a point to object 11 >>> print b # prints 10so
def func(a):
a = 10 # makes a point to 10, but this is
# not the same object which was passed
x = 11
func(x)
print x # prints 11
#!/usr/local/bin/python
def hiThere(a, b):
"""Optional docstring describing the function:
Arguments:
a - Name of person a
b - Name of person b
Using docstrings is 'best practice'
The help(x) command prints out the docstring for the function."""
print "Hello", a
print "Hi there", b
hiThere("Fred", "Jim")
x = 'mike'
y = 42
hiThere(x, y)
prints
Hello Fred Hi there Jim Hello mike Hi there 42Subroutines can return values with the return statement:
def add1(a):
print 'yawn'
return a + 1
def addsub1(a):
return (a+1, a-1)
print add1(10)
a, b = addsub1(10)
print a, b
outputs:
yawn
11
11 9
Another useful feature is that functions and subroutines can have parameters which have ``default values'' which can be overridden. This is useful for optional parameters to functions.
def greet(name, greeting='Hello'):
'''A function to greet someone'''
print greeting, name
greet('world') # prints 'Hello world'
greet('cruel world', greeting='Goodbye') # prints 'Goodbye cruel world'
"""Modules should also contain docstrings. This module contains a useful function myfunc to print hello to someone.""" def myfunc(a): print "Hello", aIn another python program, say, mainprogram.py, you could do
import mymodule
mymodule.myfunc('world')
which prints ``Hello world''. Alternatively you can do
from mymodule import *
myfunc('Fred')
Which imports everything into the current namespace. This could
be considered bad practice as things in mymodule could override things
in your main program. Avoid this by doing
from mymodule import myfunc
myfunc('foo bar')
Modules can import other modules. You can also
use modules you have created interactively:
>>> import mymodule
>>> mymodule.myfunc('goodbye')
Hello goodbye
In fact there is a massive library of useful standard modules, which I will briefly overview in Section 7.
>>> 5 == 6 # equal False >>> 5 != 6 # not equal True >>> 5 < 10 # less than True >>> 10 > 5 # greater than True >>> 5 <= 5 # less than or equal True >>> 5 >= 6 # greater or equal False >>> if 5 < 10: ... print "hello" ... hello >>> a = [1, 2, 3] >>> b = a >>> b is a # do two names point to the same object? True >>> b is not a # do they point to different objects? False >>> b = [1, 2, 3] >>> b is a # two lists with the same contents are not the same list False >>> a == b # but they are equal TrueNone is a special value meaning ``nothing''. You can test whether something is None by using is None.
This is actually how you use an if statement. Can you predict the output?
def test(a):
if a < 10: # brackets aren't required
print "a was less than 10"
elif a <= 15: # optional elif means else-if
# multiple elif are allowed
print "a was less than or equal to 15"
else: # none of the if or elif match, optional
print "a was greater than 15"
test(1)
test(12)
test(100)
You can join the various tests with and, or and
not, and also brackets:
>>> 5 < 10 and 100 > 20 and (not 'fred' == 'blogs') True
a = ['foo', 'fred', 42]
for i in a:
print i
outputs:
foo fred 42dictionaries:
a = {}
a['test'] = 42
a['bar'] = 6
for x, y in a.items():
print x, y
outputs:
test 42 bar 6Looping over files is very helpful. This example prints the sum of the numbers in a file (which are stored in separate lines).
#!/usr/bin/env python
f = open('test.dat')
sum = 0.
for line in f:
sum = sum + float(line)
print sum
You can break out of a loop with the break statement, or skip
to the next iteration with continue.
enumerate is a very useful function which counts which number of the iteration you are on
a = ['cow', 'sheep', 'horse'] for num, val in enumerate(a): print num, valoutputs:
0 cow 1 sheep 2 horse
There is also the xrange function for looping over numbers:
>>> for x in xrange(5): >>> print x*0.1 >>> 0.0 0.1 0.2 0.3 0.4Try xrange(5, 10) and xrange(10, 5, -1).
As an aside, there is a shortcut version of loops called a list comprehension which is very convenient:
>>> a = [1,2,3,4] >>> b = [i*2 for i in a] # make a new list with values*2 >>> b [2, 4, 6, 8] >>> c = [i*3 for i in a if i != 2] # new list*3, excluding 2 >>> c [3, 9, 12] >>> d = ['a', 'dairy', 'farm'] >>> e = [len(x) for x in d] >>> e [1, 5, 4]
>>> a = 1 >>> while a < 5: >>> print a >>> a = a + 1 >>> outputs: 1 2 3 4
x = []
a = 1
while a < 5:
x.append(a)
a = a + 1
print x
outputs:
[1, 2, 3, 4]
break and continue also work.
import sys
filename = 'stupid.dat'
try:
f = open(filename)
except IOError: # the file did not open
print "The filename", filename, "does not exist!"
sys.exit(1) # exit program with a fail error code
# print file contents
print f.read()
or a more complex example which reads from the user
print "Enter some numbers, or a blank line to stop"
while True:
# get a line from the user and remove any spaces around it
line = raw_input().strip()
# break out of the loop if the user enters a blank line
if line == '':
break
try:
# try to convert text to float number
a = float(line)
print "The value you entered plus 10 is", a+10.
except ValueError:
# conversion failed
print "You entered an invalid number"
You can catch different kinds of exceptions. The name of the exception
is printed out if it occurs and is not caught.
At the most simple level, you can make a class which can be used as a substitute of a C-style structure:
>>> class Empty(object): >>> pass # does nothing, but is required if empty >>> a = Empty() >>> a.fred = 'smith' >>> a.foo = [1,2,3,4] >>> print a.fred smith
A more complete example defines a constructor and some methods which operate on the data. They all access a special variable called self which refers to the object the method is applied to.
class Name(object):
"""This is an example class. A class defines a type of object.
New objects are created based on a class."""
def __init__(self, name):
"""This function __init__ is special. It is called when a new
object is constructed.
self is a special argument which refers to the object itself
name here is a parameter to create the new object, you can use
as many as you want
"""
# store name in the object
self.name = name
def sayHello(self):
"""This is a class method which prints out the stored name.
Again self is a special parameter which refers to the object
"""
print "Hello", self.name
def joinName(self, othername):
"""This is another class method which returns the original
name joined with another name."""
return self.name + " " + othername
a = Name('Fred') # makes a new object of type Name
# and calls Name.__init__(a, Fred)
a.sayHello() # calls Name.sayHello(a)
b = a.joinName('Smith') # calls function Name.sayHello(a, 'Smith')
print b # prints out "Fred Smith"
a.test = 42 # assigns attribute of class directly
print a.test # prints 42
b = Name('Joe') # makes a different object of same class
print b.joinName('Bloggs') # prints "Joe Bloggs"
James Graham has provided a much more complete example of an object in Appendix B which demonstrates useful features such as ``operator overloading''. It is a new class of number which stores its associated dimensions, so that it can be used for dimension checking.
import math print math.sin(math.pi/4) # sin(pi/4) print math.log10(1e4) # log base 10 print math.log(1e4) # natural log or from math import * print exp(1) # e**1 print sqrt(2) # sqrt(2)
>>> 1j * 1J (-1+0j) >>> 1j * complex(0,1) (-1+0j) >>> 3+1j*3 (3+3j) >>> (3+1j)*3 (9+3j) >>> (1+2j)/(1+1j) (1.5+0.5j) >>> a = 2 + 0.5j >>> a.real 2 >>> a.imag 0.5The cmath module defines mathematical functions which work on complex numbers (e.g. exp, sin, acos...). See http://www.python.org/doc/lib/module-cmath.html
import sys
print sys.argv # the parameters to a script
# from the command line
print sys.argv[0] # script name
print sys.argv[1] # first parameter
...
sys.exit(0) # normal program exit
sys.exit(1) # error program exit
import os
os.remove('foo.dat') # delete file foo.dat
os.mkdir('dir') # make directory
os.rename('foo', 'bar') # rename file
os.system('ls *.txt') # execute unix shell command IMPORTANT!
# returns 0 for success
import random print random.random() # random number 0 <= r < 1 print random.randint(1, 10) # random integer 1 <= r <= 10 a = [1,2,3,4] random.shuffle(a) # randomise list order print a print random.gauss(2, 1) # gaussian distributed random mean 2, sigma 1
import glob
x = glob.glob('*.txt') # get a list of *.txt files
print x
may output (depending on files!)
['foo.txt', 'bar.txt', 'fred.txt']
import subprocess
# does unix command, ls -l, and returns exit status
subprocess.call(["ls", "-l"])
# this executes the ls command, and reads its output
# as a list containing each line of output
>>> proc = subprocess.Popen('ls', stdout=subprocess.PIPE)
>>> lines = proc.stdout.readlines()
>>> print lines
['adam\n', 'bin\n', 'code\n', ...]
>>> import re
>>> re.findall('[0-9]+', 'Fred at 9 carrots on the 12th')
['9', '12']
>>> import urllib
>>> f = urllib.urlopen('http://www.google.co.uk')
>>> print f.readline()
<html><head><meta http-equiv="content-type" ...
This module can be used to download things from ADS, NED, for example.
>>> import numpy
>>> a = numpy.identity(4) # create identity matrix
>>> a # print it out
array([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]])
>>> a*4 - 1 # do maths
array([[ 3, -1, -1, -1],
[-1, 3, -1, -1],
[-1, -1, 3, -1],
[-1, -1, -1, 3]])
>>> numpy.invert(a) # matrix inversion
array([[-2, -1, -1, -1],
[-1, -2, -1, -1],
[-1, -1, -2, -1],
[-1, -1, -1, -2]])
>>> numpy.arange(10)*3. / 2 # make series
array([ 0. , 1.5, 3. , 4.5, ...])
>>> a[1,:] # slice matrix
array([0, 1, 0, 0])
>>> from scipy.integrate import quad >>> def square(x): ... return x**2 ... >>> quad(square, 0, 1) (0.33333333333333331, 3.7007434154171879e-15)
;; enable python
(setq auto-mode-alist
(cons '("\\.py$" . python-mode) auto-mode-alist))
(setq interpreter-mode-alist
(cons '("python" . python-mode)
interpreter-mode-alist))
(autoload 'python-mode "python-mode" "Python editing mode." t)
To get this more easily, you can copy my .emacs file to get
support (on the Sun system):
> cp /home/jss/.emacs /home/yourusername/
class PhysScalar(object):
"""A physical quantity with an set of
dimensions e.g. Length, Distance, etc. Operations involving
physical scalars are checked for matching dimensions. This does
not account for the possibility that the units are wrong
e.g. adding feet to metres"""
ValidDimensions = ["L", "M", "T", "K"]#Length, mass, time, temperature
def __init__(self, value, dimensions):
"""value - The magnitude of the quantity
dimensions - a dict of the form {dimension:power}
e.g. {"M":2} for an area
"""
self._value = float(value)
self.dimensions=dimensions
#Check all the dimensions are valid
#This is strictly unnecessary b
for item in dimensions.keys():
if item not in self.ValidDimensions:
raise ValueError, "Unrecognised dimension %s"%str(item)
def __str__(self):
"""This method is called when we try to convert something to a
string"""
#Create a string like L**2 T**-1 for the dimensions
dimensionsStr = " ".join(["%s**%s"%(item[0],str(item[1]))
for item in self.dimensions.items()])
return "%s %s"%(str(self._value), dimensionsStr)
def __repr__(self):
"""This method is used to represent the current object as a sting
e.g. on the interactive prompt"""
return "PhysConst(%f, %s)"%(self._value, str(self.dimensions))
#We use "magic" methods to define operators that will allow us to add,
#subtract and multiply PhysConst objects. These operations all return
#new objects
def __add__(self, other):
"""Add two quantities if they have the same dimensions.
Also allows the addition of dimensionless quantities
i.e. plain python floats"""
#Check if the object being added has a 'dimensions' attribute
#If it does we assume it can be added by comparing values
if hasattr(other, 'dimensions'):
if self.dimensions == other.dimensions:
return PhysScalar(self._value+other._value, self.dimensions)
else:
raise TypeError, "Incompatible dimensions"
else:
return PhysScalar(self._value+other, self.dimensions)
def __sub__(self,other):
"""Subtraction self-other"""
#Implement subtraction as addition with a minus;
#this isn't efficient but reduces code
return self.__add__(-1*other)
def _operator(self, other, valueFunc, dimFunc, right=False):
"""Base function used for multiplication and division.
valueFunc(selfValue, otherValue): a function takes two parameters and
implements the operation (e.g. multiplcation, division) on the values
dimFunc(power1, power2): a function that takes two parameters and
implements the operation on the dimensions
right: True if self on the rhs of the operation
e.g. for multiplcation valueFunc(x,y) should return x*y and
dimFunc(x,y) should return x+y
Limitations - No support for self on the rhs if the lhs object has a
dimensions attribute but does not implement operators itself
"""
#Dimensions of the output object
newDims = {}
if hasattr(other, 'dimensions'):
#Add all the dimensions from the self to the output dimensions
newDims.update(self.dimensions)
#Now add the dimensions from other
for key,value in other.dimensions.iteritems():
if key in newDims:
newDims[key] = dimFunc(newDims[key], value)
if newDims[key] == 0:
#Delete dimensions that have zero value
del newDims[key]
else:
newDims[key] = dimFunc(0.0, value)
return PhysScalar(valueFunc(self._value,other._value), newDims)
else:
for key,value in self.dimensions.iteritems():
if right:
value = dimFunc(value, 0.0)
newDims[key] = value
return PhysScalar(valueFunc(self._value,other), newDims)
def __mul__(self,other):
def add(x,y):
return x+y
def mult(x,y):
return x*y
return self._operator(other, mult, add)
def __div__(self,other):
def subtract(x,y):
return x-y
def divide(x,y):
return x/y
return self._operator(other, divide, subtract)
#These functions define operations other + self
__radd__ = __add__
#other*self
__rmul__ = __mul__
#other-self
def __rsub__(self, other):
"""Right hand subtraction other-self"""
return -1*self.__sub__(other)
#For python 2.5
__truediv__ = __div__
def __rdiv__(self, other):
"""Right hand division used for e.g. 2.0/PhysScalar(2.0, {"L":1}"""
def rsubtract(x,y):
return y-x
def rdivide(x,y):
return y/x
return self._operator(other, rdivide, rsubtract, True)
def __pow__(self, power):
"""Powers e.g. PhysScalar(2.0, {"L":1.0})**2"""
newDims = {}
for key, value in self.dimensions.iteritems():
newDims[key] = value * power
return PhysScalar(self._value**power, newDims)
"""
#Examples
>>> a = PhysScalar(2.0, {"M":1,"L":2,"T":-2})
>>> a
PhysConst(2.000000, {'M': 1, 'L': 2, 'T': -2})
>>> print a
2.0 M**1 L**2 T**-2
>>> a**2
PhysConst(4.000000, {'M': 2, 'L': 4, 'T': -4})
>>> b = PhysScalar(1.0, {"M":1,"L":2,"T":-2})
>>> a+b
PhysConst(3.000000, {'M': 1, 'L': 2, 'T': -2})
>>> a-b
PhysConst(1.000000, {'M': 1, 'L': 2, 'T': -2})
>>> a+2 #We allow adding/subtracting dimensionless scalars but we could prevent
this
PhysConst(4.000000, {'M': 1, 'L': 2, 'T': -2})
>>> c = PhysScalar(2.5, {"K":-2})
>>> a*c
PhysConst(5.000000, {'K': -2.0, 'M': 1, 'L': 2, 'T': -2})
>>> a/c
PhysConst(0.800000, {'K': 2.0, 'M': 1, 'L': 2, 'T': -2})
>>> 2/a
PhysConst(1.000000, {'M': -1.0, 'L': -2.0, 'T': 2.0})
>>> a+c
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "dimension_check.py", line 51, in __add__
raise TypeError, "Incompatible dimensions"
TypeError: Incompatible dimensions
"""
This document was generated using the LaTeX2HTML translator Version 2008 (1.71)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -split 0 -font_size 10pt -no_navigation python_123.tex
The translation was initiated by Jeremy Sanders on 2011-10-02