When writing computer commands that you can type, the font will change to command
. For example, x=2**3.4/5
is a command that can be typed in.
When we talk about a general command, or some piece of missing data that you should complete, we will use angle brackets as in <command>
or <variable>
. For example, <location>
could mean "Southampton"
.
When showing actual commands as typed into Python, they will start with In [<number>]:
. This is the notation used by the IPython console. The <number>
allows you to refer to previous commands more easily. The output associated with that command will start with Out [<number>]:
.
When displaying code, certain commands will appear in different colours. The colours are not necessary. They highlight different types of command or variable. When using the spyder editor you may find the colours match up and are useful: if not, either ignore them or switch them off.
Start with using Python as a calculator. Look at the console in the bottom right part of spyder. Here we can type commands and see a result. Simple arithmetic gives the expected results:
In [1]:
2+2
Out[1]:
In [2]:
(13.5*2.6-1.4)/10.2
Out[2]:
If we want to raise a number to a power, say $2^4$, the notation is **
:
In [3]:
2**4
Out[3]:
There is an issue with division. If we divide an integer by an integer, Python 3.X
will do real (floating point) division, so that:
In [4]:
5/2
Out[4]:
However, Python 2.X
will do division in the integers:
In []: 5/2
Out []: 2
If you are using Python 2.X
and want the division operator to behave in this way, start by using the command
In [5]:
from __future__ import division
Then:
In [6]:
5/2
Out[6]:
If you really want to do integer division, the command is //
:
In [7]:
5//2
Out[7]:
Further mathematical commands, even things as simple as $\log$ or $\sin$, are not included as part of basic Python:
In [8]:
log(2.3)
In [9]:
sin(1.4)
First, note the way that errors are reported: we'll see this a lot, and there's useful information there to understand. It's telling us
The language Python uses takes some getting used to, but it's worth the effort to read these messages, and think about what it's trying to say. Here it's pointing to the line where the problem happened, and saying "I don't understand what this command is!".
Going back to the mathematics, we obviously want to be able to compute more mathematical functions. For this we need a module or package.
Anything that isn't provided by the base Python can be provided by modules and packages. A module is a file containing functions and definitions that we can include and use in our code. A package is a collection of modules. They're not included by default, to reduce overhead. They're easy to write - we will write our own later - and easy to include.
To use a package we must import it. Let's look at the math
package.
In [10]:
import math
In [11]:
math.log(2.3)
Out[11]:
In [12]:
math.sin(1.2)
Out[12]:
To use the package we've typed import <package>
, where in this case <package>
is math
. Then we can use functions from that package by typing <package>.<function>
, as we have here when <function>
is either log
or sin
.
The "dot" notation may seem annoying, and can be avoided by specifying what functions and constants we want to use. For example, we could just get the log
function and use that:
In [13]:
from math import log
In [14]:
log(2.3)
Out[14]:
However, the "dot" notation is useful, as we often find the same symbol or name being used for many different purposes. For example, the math
package contains the mathematical constant $e$ as math.e
:
In [15]:
math.e
Out[15]:
But there is also the electron charge, usually denoted $e$, which is in the scipy.constants
package:
In [16]:
import scipy.constants
In [17]:
scipy.constants.e
Out[17]:
To avoid these name clashes we can import something as a different name:
In [18]:
from math import e
In [19]:
from scipy.constants import e as charge_e
In [20]:
e
Out[20]:
In [21]:
charge_e
Out[21]:
You will often see this method used to shorten the names of imported modules or functions. For example, standard examples often used are:
In [22]:
import numpy as np
In [23]:
import matplotlib.pyplot as plt
The commands can then be used by typing np.<function>
, or plt.<function>
, which saves typing. We would encourage you not to do this as it can make your code less clear.
A variable is an object with a name and a value:
In [24]:
x = 2
In standard programming all variables must have a value (although that value may be a placeholder to say "this variable doesn't have a reasonable value yet"). Only symbolic packages can mirror the analytical method of having a variable with no specific value. However, code can be written as if the variables had no specific value.
For example, we cannot write
In [25]:
x = y**2
as y
does not have a specific value yet. However, we can write
In [26]:
y = 3.14159627
In [27]:
x = y**2
In [28]:
print(x)
and get a sensible result, even though we have written the exact same line of code, as now y
has a specific value.
Note that we have defined the variable x
twice in rapid succession: first as an integer (x=2
) and next as a floating point number, or float (the computer's implementation of a real number, using x=y**2
, where y
is a float). Not all programming languages allow you to do this. In a statically typed language you have to say whether a variable will be an integer, or a float, or another type, before you define it, and then it cannot change. Python is dynamically typed, so any variable can have any type, which can be changed as we go.
A variable is an object with a name, but not just any name will do. Python has rules which must be followed, and conventions that should be followed, with a few gray areas.
Variables must
!@#$%^&*()\|
)So the following are valid:
In [29]:
half = 1.0/2.0
In [30]:
one_half = 1.0/2.0
but the following are not:
In [31]:
one half = 1.0/2.0
In [32]:
1_half = 1.0/2.0
In [33]:
one!half = 1.0/2.0
Variables should
More detail can be found in PEP8.
Variables may contain some unicode characters, depending on Python version and operating system. In Python 3
you can include accents or extended character sets in variable names:
rôle = 5
π = math.pi
However, these tricks are not always portable between different Python versions (they aren't guaranteed to work in Python 2
), or different operating systems, or even different machines. To ensure that your code works as widely as possible, and that the methods you use will carry over to other programming languages, it is recommended that variables do not use any extended characters, but only the basic latin characters, numbers, and underscores.
One thing that may seem odd, or just plain wrong to a mathematician, is two statements like
In [34]:
x = 2
In [35]:
x = 3
How can x
equal both 2 and 3? Mathematically, this is nonsense.
The point is that, in nearly every programming language, the =
symbol is not mathematical equality. It is the assignment operation: "set the value of the variable (on the left hand side) equal to the result of the operation (on the right hand side)". This implies another difference from the mathematical equality: we cannot flip the two sides and the line of code mean the same. For example,
In [36]:
3 = x
immediately fails as 3
is not a variable but a fixed quantity (a literal), which cannot be assigned to. Mathematically there is no difference between $x=3$ and $3=x$; in programming there is a huge difference between x=3
and 3=x
as the meaning of =
is not the mathematical meaning.
To get closer to the standard mathematical equality, Python has the ==
operator. This compares the left and right hand sides and says whether or not their values are equal:
In [37]:
x == 3.0
Out[37]:
However, this may not be exactly what we want. Note that we have assigned x
to be the integer 3, but have compared its value to the float 3.0. If we want to check equality of value and type, Python has the type
function:
In [38]:
type(x)
Out[38]:
In [39]:
type(x) == type(3.0)
Out[39]:
In [40]:
type(x) == type(3)
Out[40]:
Direct comparisons of equality are often avoided for floating point numbers, due to inherent numerical inaccuracies:
In [41]:
p = 2.01
In [42]:
p**2-4.0401
Out[42]:
In [43]:
p**2 == 4.0401
Out[43]:
We will return to this later.
Making mistakes and fixing them is an essential part of both mathematics and programming. When trying to fix problems in your code this process is called debugging. As you've been following along with the code you will have made "mistakes" as there are intentionally broken commands above to show, for example, why one!half
is not a valid variable (and you may have made unintentional mistakes as well).
There are a number of techniques and strategies to make debugging less painful. There's more detail in later chapters, but for now let's look at the crucial first method.
When you make a mistake Python tells you, and in some detail. When using IPython in particular, there is much information you can get from the error message, and you should read it in detail. Let's look at examples.
A syntax error is when Python cannot interpret the command you have entered.
In [44]:
one half = 1.0/2.0
This example we saw above. The SyntaxError
is because of the space - one half
is not a valid variable name. The error message says invalid syntax
because Python can't interpret the command on the left of the equals sign. The use of the carat ^
points to the particular "variable" that Python can't understand.
In [45]:
x = 1.0 / ( 2.0 + (3.0 * 4.5)
This example is still a SyntaxError
, but the pointer (^
) is indicating the end of the line, and the statement is saying something new. In this statement unexpected EOF while parsing
, "EOF
" stands for "end of file". This can be reworded as "Python was reading your command and got to the end before it expected".
This usually means something is missing. In this case a bracket is missing - there are two left brackets but only one right bracket.
A similar example would be
In [46]:
name = "This string should end here...
In this case the error message includes "EOL
", standing for "end of line". So we can reword this message as "Python was reading your command and got to the end of the line before the string finished". We fix this by adding a "
at the end of the line.
Finally, what happens if the error is buried in many lines of code? This isn't the way we'd enter code in the console, but is how we'd deal with code in the notebook, or in a script or file:
In [47]:
x = 1.0
y = 2.3
z = 4.5 * x y + y
a = x + y + z
b = 3.4 * a
In [48]:
my_variable = 6
my_variable_2 = 10
x = my_variable + my_variable_3
It usually means you've made a typo, or have referred to a variable before defining it (sometimes by cutting and pasting code around). Using tab completion is a good way of minimizing these errors.
Note that there is a difference between syntax errors and other errors. Syntax errors are spotted by the code before it is run; other errors require running the code. This means the format of the output is different. Rather than giving the line number and using a carat ("^
") to point to the error, instead it points to the code line using "---->
" and adds the line number before the line of code.
A range of "incorrect" mathematical operations will give errors. For example
In [49]:
1.0/0.0
Obviously dividing by zero is a bad thing to try and do within the reals. Note that the definition of floating point numbers does include the concept of infinity (via "inf
"), but this is painful to use and should be avoided where possible.
In [50]:
math.log(0.0)
A ValueError
generally means that you're trying to call a function with a value that just doesn't make sense to it: in this case, $\log(x)$ just can't be evaluated when $x=0$.
In [51]:
10.0**(10.0**(10.0**10.0))
An OverflowError
is when the number gets too large to be represented as a floating point number.
Having read the error message, find the line in your code that it indicates. If the error is clear, then fix it. If not, try splitting the line into multiple simpler commands and see where the problem lies. If using spyder, enter the commands into the editor and see if the syntax highlighting helps spot errors.
Remember that $n! = n \times (n - 1) \times \dots \times 2 \times 1$. Compute $15!$, assigning the result to a sensible variable name.
Using the math
module, check your result for $15$ factorial. You should explore the help for the math
library and its functions, using eg tab-completion, the spyder inspector, or online sources.
Stirling's approximation gives that, for large enough $n$,
$$ n! \simeq S = \sqrt{2 \pi} n^{n + 1/2} e^{-n}. $$Using functions and constants from the math
library, compare the results of $n!$ and Stirling's approximation for $n = 5, 10, 15, 20$. In what sense does the approximation improve (investigate the absolute error $|n! - S|$ and the relative error $|n! - S| / n!$)?