year 1 physics/astrophysics laboratory
TRANSCRIPT
SCHOOL OF CHEMICAL AND PHYSICAL SCIENCES
Year 1 Physics/Astrophysics Laboratory
Computer Programming with Python
A. Mahendrasingam and D.E. McLaughlin
Physics/Astrophysics
Lennard Jones Building
Year 1 Computing Lab Manual
Student Nameβ¦β¦β¦β¦β¦β¦β¦β¦β¦β¦β¦...β¦β¦β¦β¦β¦β¦..
Keele University Physics/Astrophysics Laboratory Programming with Python Page 1
Contents
INTRODUCTION .............................................................................. 2 PYTHON: WORKSHEET 1 ............................................................... 7
Problem 1.1 .............................................................................................. 12 Problem 1.2 .............................................................................................. 14
PYTHON: WORKSHEET 2 ............................................................ 17 Problem 2.1 .............................................................................................. 19 Problem 2.2 .............................................................................................. 21
PYTHON: WORKSHEET 3 ............................................................ 22 Problem 3.1 .............................................................................................. 23 Problem 3.2 .............................................................................................. 30
PYTHON: WORKSHEET 4 ............................................................ 30 Problem 4.1 .............................................................................................. 36
PYTHON: WORKSHEET 5 ............................................................ 37 Problem 5.1 .............................................................................................. 38 Problem 5.2 .............................................................................................. 38
Keele University Physics/Astrophysics Laboratory Programming with Python Page 2
INTRODUCTION
This series of labs is meant to give you an introduction to the basics of scientific programming,
using the Python language. Python is relatively quick-to-learn and easy-to-use, and yet still fairly
powerful. It is a somewhat βnewβ language (first released in 1991), but it has grown rapidly and it is
finding wider and wider application in industry and in academia. It is open source, freely available
and well-documented (see www.python.org), and it can be run on any standard platform
(Windows, Mac, Linux/Unix). Advanced, scientific calculations, simulations and other applications
are still generally done using other high-level languages (e.g., Fortran, C or C++), which are older
and more established, have less frequent updates to the core standards, and also have numerous
and varied extensions (libraries) that provide for flexibility and customisation and the modification
of βlegacyβ programs. Moreover, Python codes execute more slowly in general than equivalent
codes written in these other languages, because Python programs are interpreted by a computer
at the time they are run, rather than being compiled into machine-language versions first. On the
other hand, compiled languages often have more rigidly structured programming rules and
apparently more idiosyncratic syntax than Python has. Writing in Python is about as close as
modern computer programming comes to writing in English. This makes Python an ideal language
in which to focus on learning and testing the ideas behind some basic procedures and methods, or
algorithms, which are generically useful for solving problems in physics and mathematics.
GETTING STARTED: BASIC TOOLS
The version of Python that you will use to do the exercises in these labs is Python 3.5. It is already
installed on the Windows machines in the large Faculty of Science PC Lab in the Lennard-Jone
Building (LJ 0.26) and in the smaller Physics and Astrophysics PC Lab in Lennard-Jones (LJ
1.27).
To start up Python 3.5 on a machine in LJ 0.26, go into the Physics folder on your Desktop
and double-click on the shortcut called python 3.5. This opens an interactive IDLE shell, or
window. IDLE is Pythonβs built-in Integrated Development Environment and is what you will be
using to complete all of the work in these labs: testing and de-bugging bits of code in real time,
and writing, saving and running complete programs. If you wish to run Python under Windows
in LJ 1.27, look instead in the Start Menu for a folder called Python 3.5 (it should be listed
under βAll Programsβ). Inside this folder is a shortcut called IDLE (Python GUI) and double-
clicking this will open an interactive IDLE shell.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 3
All of the executables, libraries and dll files, etc. for the Windows installation of Python 3.5 are
stored in the folder C:/Python35. You should not have to go to this folder just to start up IDLE or
another Python executable, and you should not open directly any of the other files associated with
the installation. However, you need to be aware that the C:/Python35 folder will (at least initially)
be the default location where Windows tries to store the programs that you write and save to disk
from inside IDLE. But you should never save any material whatsoever in the C:/Python35 folder. When you come to save programs that you have written, or when you open and run
programs that you saved previously, go instead to a location that you decide on your S: drive.
To get a flavour of the Python language and working in the IDLE environment, and (crucially!) to
see how to save complete programs and run them later either from within IDLE or outside it, work
carefully through the rest of this Introduction before you move on to any of the subsequent
Worksheets. Here, and throughout, pay close attention to exactly what you type. Python is
case-sensitive and so (for example) recognises a command such as βprintβ, but not βPrintβ or
βPRINTβ. Also, quotation marks and spaces can have very specific meanings, and leaving some
out or inserting extra ones may have unintended consequences.
You can always access the full, offline documentation for your version of Python, including
detailed Table of Contents and Index and Search capabilities, by selecting βPython Docsβ from
under the βHelpβ tab that is found along the top of any IDLE shell. A useful tutorial is also available
within this documentation, along with much more advanced help and advice. Documentation and
tutorials are also available online, at www.python.org, but these may be for versions of the
language other than the one installed on your computer.
THE IDLE SHELL AND A SIMPLE FIRST PROGRAM
Start IDLE using the appropriate shortcut as described above. A window (or shell) will appear that
is mostly empty, with your cursor beside a command-line prompt of three greater-than signs (>>>)
near the top. At the prompt, type
print("Hello.")
and press the <Enter> key. Here print() is an example of a built-in function in Python: the word
actually invokes a piece of code βunder the hoodβ of the language, which takes a specific
argument (everything in parentheses after the function nameβin this case, βHello.β) and performs
a well-defined action upon it. In this case, obviously, the action is just to print the argument to the
Keele University Physics/Astrophysics Laboratory Programming with Python Page 4
IDLE screen. Note that the quotation marks around the word βHelloβ do not get printed out,
however. They are there to identify just what is supposed to be printed. (Technically, quotation
marks identify any sequence of characters between them as a string constant or literal.) Try typing
print(Hello.) without the quotation marks to see an example of a syntax error.
The names, actions and syntaxes of built-in functions in Python can be looked up in the Python
Manual accessed through the Help tab (i.e., βHelp > Python Docsβ) in the menu at the top of the
IDLE window.
Now, with your cursor at the prompt on the next free line in IDLE, type
name= input("What is your name? ")
and press <Enter>. You will see the cursor flashing just after the question mark asking for your
name. This means that the program is now waiting for input, so type in your name and press
<Enter> again. This returns you to the IDLE command prompt (>>>) and frees up the terminal for
further commands.
Here, input is another built-in function. This one takes an argument (βWhat is your name? β), prints
that to the screen, and then waits for input directly from the user. Everything typed by the user,
until <Enter> is pressed again, is taken by the function and stored in memory as the value of a
string variable that is called (in this example) name. After the user has pressed <Enter> to
complete the input, nothing more is done with the input, unless and until the variable is referred to
in later statements or commands. In principle, almost any word (excepting only those that already
refer to built-in functions) can be used to refer to a variableβthis one did not have to be called
nameβbut it is good practice to choose words that have fairly obvious meanings and are likely to
make sense both to you and to any other readers or users of your code. (Again, Python is case-
sensitive; so, for example, a variable called name is distinct from one called Name.)
Finally, type
print("Goodbye " + name + ".")
and press <Enter>. Now the argument to the built-in print() function is the concatenation of two
separate string constants (each contained in quotation marks) on either side of the value of the
variable name (written without quotation marks) that you entered just above. The plus-signs inside
the parentheses join the constants and the (value of the) variable together into a single string for
printing.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 5
You should now have some feel for working in the IDLE shell. One thing you will have noticed, as
you were entering the few lines of code above, is that various words appear in different colours.
This is done in order to distinguish explicitly between (for example) built-in functions, constants,
variables and other fundamental elements of the language. These visual distinctions can be very
helpful when youβre trying to build up a real code without mistakes, and/or to identify problems of
logic or syntax in an existing program. The colouring follows a pre-defined default scheme. You
can see the details by clicking on βOptions > Configure IDLE > Highlightingβ in the menu at the top
of the IDLE window. (Exit this menu item by clicking βCancelβ rather than βOkβ or βApplyβ if you
donβt want to change the defaults.)
You should also try moving your cursor around inside the IDLE shell, using the mouse and the
arrow keys, to see how you can repeat and/or edit commands that were entered previously.
SAVING AND RUNNING PROGRAMS
Technically, the three Python lines (or statements) that you entered into IDLE above constitute a
program. But no program is particularly useful until it has been stored in a file on disk that can be
run (and/or edited, expanded, etc.) at will, at any later time.
IDLE gives you access to the interactive mode of Python, which you can use to try out short
ideas and get immediate feedback, quickly check that the results are what you expect, see
warnings about syntax errors, and so on. To save a program that can be run later, independently
of anything else you do in IDLE, you need to work in script mode, which requires opening a
different shell or window. You can do this from within IDLE and then have both shells open at
once, using the interactive shell to try things out and the scripting shell to contain the final code
that will be saved to disk.
Go to the βFileβ menu at the top of the interactive IDLE shell, and select βNew Windowβ. A
completely empty shell will appear that looks nearly the same as your interactive one, except it
doesnβt have a command prompt (>>>) in it, and the menu items across the top include βFormatβ
and βRunβ tabs rather than the βShellβ and βDebugβ menu tabs that are in the interactive shell. This
new window is your scripting shell. Type the following program into it:
print("\n" + "Hello.") name=input("What is your name? ") print("Goodbye " + name + ".") stop=input("\n" + "Press <Enter> to quit. " + "\n")
Keele University Physics/Astrophysics Laboratory Programming with Python Page 6
There are some minor new elements in this program. One is the presence of β\nβ at a few places
inside the arguments to one of the print() statements and one of the input() statements. Each
instance of β\nβ represents an instruction to print an entirely blank line to the screen, just to make
the output of the code look cleaner and easier to read (the n is for βnewβ line; the \ makes the n be
interpreted in this way rather than printed literally as a letter of the alphabet). Second, the final line
of the program is present simply to force the program to βhangβ after doing its main work, and wait
until the user physically hits <Enter> before it finishes completely. This is done in order to prevent
programs exiting and closing windows too rapidly (before output can be read!) when they are run
outside of IDLE. Finally, the last line of the program is set off from the others by a blank line before
it. This is not required for any reason by Python; but it is not disallowed, either, and leaving some
lines intentionally blank like this can help to organise a code and make it more readable.
Now that the program has been written in the scripting shell, it can be saved. Go to the βFileβ menu
tab at the top of the scripting shell, left-click on it and select βSave Asβ¦β. A window will come up,
which you can use to navigate to any folder you like to save your work, and to give a name to the
saved program file. Pythonβs initial default option for the save folder is C:/Python35 but again: you
should not save your work in this folder. Navigate instead to some location on your S: drive and
keep your programs there. (Just be sure you remember where it is!) The name of the program file
can be anything you choose, but it must end in the extension .py and you will need to type this
extension explicitly as part of the filename. A saved program can then be run in either of two ways.
To run a saved program within IDLE: If the program is already open in a scripting shell, simply
choose βRun > Run Moduleβ from the menu options at the top of the scripting shell. The output
from the code will appear in an interactive IDLE shell, which is also where you can enter any input
that the program asks for. If an interactive IDLE shell is open but no scripting shell is open, choose
βFile > Openβ from the IDLE menu options and navigate to the location of the saved program on
your S: drive. The program will then open in a new scripting shell with a Run tab in its top menu.
To run a saved program directly from Windows, bypassing IDLE and scripting shells altogether: Go to the folder on your S: drive that contains the program file and double-click the file
name (which, again, must end in the extension .py). A DOS window (CMD Prompt) will be
opened to display the program output and to accept any input that the program wants. This
window will be closed instantly when the last statement of the program has been executed (hence
the need for the final βhangβ line that you wrote above).
Try both of these methods of running your saved program and make sure that both work perfectly
for you before you proceed to Worksheet 1.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 7
PYTHON: WORKSHEET 1 Open an interactive IDLE shell and a new scripting window. Type the following program into the
scripting window exactly as it is written here:
# Program to solve a quadratic equation, ax^2 + bx + c = 0 import cmath # functions in the cmath module work with complex numbers a = input(" Please enter the number a: ") b = input(" Please enter the number b: ") c = input(" Please enter the number c: ") # a=float(a) b=float(b) c=float(c) # disc = b**2 - 4*a*c disc = cmath.sqrt(1.0*disc) x1 = (-b + disc)/(2.0*a) x2 = (-b - disc)/(2.0*a) print("\n" + "Solutions are: x = " + str(x1) + " and x = " + str(x2)) stop=input("\n" + "Press <Enter> to quit." + "\n")
In order to write a computer program such as this, one has to break down the method of solution
of a problem into a series of sequential steps. This is called an algorithm. It is important to follow
through this program line-by-line to make sure you understand how it solves the quadratic-
equation problem within Python.
First, anything written following a hash (#) on a line does not form part of any statement or
command on the rest of the line and is not executed as code; instead, it is treated as a comment
and is ignored by the Python interpreter. You may (should!) put comments liberally in your
programs to help yourself and others understand them.
Second, at the top of the program is the statement import cmath. The word import tells Python
that the program is going to make use of one or more built-in functions that are kept in a particular
collection of related functions (known as a module), which, while standard to the language, are not
made available automatically to all new programs but have to be explicitly βloaded inβ to them. In
this case, the module needed is cmath, which includes a number of mathematical functions for
operations with complex numbers. You can read about this module in the Python Manual,
available through βHelp > Python Docsβ in the IDLE menu. The function from cmath that is needed
Keele University Physics/Astrophysics Laboratory Programming with Python Page 8
in the program above is the square-root function, sqrt(). Note that the only reason for importing
the cmath moduleβas opposed to the math module, which contains the square-root and other
functions for operating on real numbersβis the possibility that the discriminant of a quadratic
equation may be negative, and its square root imaginary.
Following the import statement is a statement that invokes the built-in function input(), which is
not actually part of cmath but is always available to any program, no matter what other modules
have or have not been imported. Note that, as written here, the input() statement is asking for
numerical value to be entered and values that will be stored in the variables called a, b and c.
The concept of a variable is a very important one. In the program above, the quantities a, b, c, disc, x1 and x2 are numerical variables. Think of the memory of the computer as a series of
boxes, each of which can contain one piece of data. Whenever you introduce a new variable name
in a program, it will be attached to one box in memory, and thereafter in the program the variable
name will refer to the value (whether a number or a string) stored in that box. As mentioned
before, variable names can be almost anything you like, as long as they start with an alphabetic
character and do not take the name of any built-in function. Always try and give variables
meaningful names.
Variables can be used together with constants and operators to make expressions. An example
of a numerical expression in the above program is b**2 - 4*a*c. (There are also several string
expressions, in the arguments to the input() and print() functions.) When Python encounters an
expression involving variables, it works out its value using the current values of the variables. The
mathematical operators for numerical variables are +, β, * (multiply), / (divide) and ** (raise to
the power). Python obeys the usual order of operations, and brackets can always be used to make
the mathematical meaning of any expression unambiguous. For string variables and constants, an
important operator is + (concatenation), which we have already encountered.
Statements such as a,b,c = input(β¦) or disc = b**2 - 4*a*c assign the values of expressions to
variables. This means the expression (or function) on the right-hand side of an equals sign is
evaluated (or performed), and the result is put into the memory referenced by the variable(s) on
the left-hand side. This creates the variables if they hadnβt been referred to previously in a
program, or it gives them new values if they had been assigned values earlier in the program.
The final unfamiliar pieces of syntax in this program involve the built-in functions cmath.sqrt() and
str(). It should be clear what the function sqrt() refers to; the cmath joined to the front of this by a
dot is simply identifying the module (imported in the first line of the program) that contains the
sqrt() function. The str() functionβwhich, like print() and input(), is always available and doesnβt
Keele University Physics/Astrophysics Laboratory Programming with Python Page 9
require any modules to be importedβconverts numerical values (including complex numbers) into
strings, which is done here with x1 and x2 inside the argument to the print() function.
Save your quadratic-equation program to a file with a meaningful name (ending in .py) in the
location you have chosen on your S: drive, and run it in IDLE to see how it works. (Note that if you
edit a program in the scripting window and then select βRun > Run Moduleβ from the window menu
before saving the changes, you should be prompted to save the file first.) Think up test cases:
quadratic equations, ax2 + bx + c = 0 with particular values of a, b and c, whose roots you know.
Pick cases with both real and complex roots, and see if your program gives the right answers.
The arguments of functions can be entire expressions, as well as variable names. Use this fact to
modify the program above, eliminating its third statement (not counting comments). Check that the
program still works properly.
CONSTANTS, VARIABLES AND EXPRESSIONS Type the following statements one by one into an interactive IDLE shell. There is no need to enter
them into a scripting shell to be saved as a program; just make sure that you understand how
Python evaluates each expression, and what point is being is being made in each case.
print(6+10/2) print((6+10)/2) print(2.5/1.0e12) print(12/6*2) print(12/(6*2)) print(1.0e3) print(27**113)
Next, try the following set of statements:
a = 4 print("a+5") print(a+5) a = a+5 print(a+5)
Then, pay close attention to the results of each of the following: print(2/3) print(2.0/3.0) a=2.0/3.0 b=2/3 print(a*b) print((2.0/3.0)*2/3) print(1/a) print(1/(2/3))
Keele University Physics/Astrophysics Laboratory Programming with Python Page 10
In Python (or, in fact, in any other scientific programming language), there is a fundamental
difference between expressions like 2.0/3.0 and those like 2/3. It is crucial that you understand
exactly what the difference is and can explain the results of each of the print() statements just
aboveβ¦ so make sure that you do (i.e., ask if you donβt).
Finally, to remind yourself of the difference between string constants and variables:
Hello = "Goodbye" print("Hello") print(Hello)
BUILT-IN FUNCTIONS There are many functions built into Python that can be used in expressions. As already seen
above, some built-in functions β like print(), input() and str() β are always available and can be
used in any program without any further thought; while others β such as sqrt() β are only
included in collections of related functions, or modules, that must be imported explicitly into a
program before the function can be used. You can get an overview of the βalways-availableβ built-
in functions and the various functions in modules that need importing, from the Python
documentation: Select βHelp > Python Docsβ in the IDLE menu. In the Documentation that comes
up, click through to the Library Reference and go to Section 2 there.
Not surprisingly, one module that you will frequently need to import into your programs is the math
module, which includes a large number of functions for operations involving real numbers (that is,
the arguments to these functions must be numerical variables, constants or expressions with real
values; they cannot be complex). The functions in this module are listed and described in detail
under Section 9.2 of the standard Library Reference in the Python Documentation.
Below are several one-line examples using functions from the math module. Try each of these in
the interactive IDLE shell to see what it does, and read more about the functions in the
Documentation if anything is unclear. You may want to make notes for yourself of what does what
in the following, and you may also want to put these lines into a program that you can save and
run later, for reference.
First, whether in IDLE or in a scripting window for a program to be saved, you need to type
import math
Then try the following:
Keele University Physics/Astrophysics Laboratory Programming with Python Page 11
pi = 3.1416 print(pi) print(math.pi) print(int(math.pi)) print(math.sin(pi/6)) print(math.sin(pi/6)**2 + math.cos(pi/6)**2) angle = math.atan(1.0) print(180.0*angle/math.pi) print(math.degrees(angle)) angle = 45.0 print(math.radians(angle)) print(math.e) print(math.e**(2.0)) print(math.exp(2.0)) print(math.log(7.389)) print(math.log10(7.389)) print(math.sqrt(4.0)) print(math.sqrt(-4.0)) print(math.fabs(-7.7)) print(math.fabs(7.7)) print(math.factorial(3.0)) print(math.factorial(3)) print(math.factorial(3.1)) print(int(7.7)) print(int(7.7+0.5)) print(math.trunc(7.7)) print(math.trunc(7.7+0.5)) print(int(-5.0/3.0)) print(int(-5/3))
In the above, every built-in function that is referenced with βmath.β in front of the function name is
found only in the math module and will not be available to any program unless the statement
import math appears before the function is invoked. The built-in function int() is used often
enough that it is actually outside the math module and is one of those that is always available to
any program, without any importing. (What does int() do?)
Also, in the above, pi, angle, a and b are variables. The quantities math.pi and math.e may look
at first glance like built-in functions in the math module, but in fact they are built-in constants, with
βexactβ values of Ο and e stored to the limits of machine precision. Actually, you can tell that these
are not functions by the lack of parentheses () immediately after their names: math.pi and math.e
do not take arguments like functions do.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 12
Now that you have experience with some key basics of Python and the use of built-in
mathematical functions, you can write a program to solve the following simple physics problem.
You will need to import math near the beginning of the program, and donβt forget to type
everything in a scripting window so you can save the final program to your S: drive and run it later.
Use comments liberally in your program to help you remember (and explain to others) the logic
behind your algorithm, the meanings of your variable names, and so on.
LOGICAL EXPRESSIONS Logical expressions test relationships between numerical or string variables or expressions and
evaluate to either True or False. They are formed using the following relational operators:
> (greater than) < (less than) >= (greater than or equal to) <= (less than or equal to) == (equal to) != (not equal to)
With strings, βgreater thanβ and βless thanβ refer to alphabetical order. Logical expressions may be
joined with and and or, or negated with not. To see how these work, type the following in IDLE:
print( 7 > 6 ) print( 6 >=7 ) print( "seven" > "six" ) print ("seven" <= "six") print( 2+2 == 4 ) print( 3+3 > 7 ) print( (2+2 == 4) or (3+3 > 7) ) print( (2+2 == 4) and (3+3 > 7) ) print( (2+2 == 4) and (not (3+3 > 7)) )
Problem 1.1
If a projectile is fired with speed v from the ground at an angle ΞΈ to the horizontal, the
horizontal distance (range) it travels is given by R = (v2/g) sin(2ΞΈ), where g is the
acceleration due to gravity.
Write a program that will take the values of ΞΈ (assumed to be in degrees) and v (assumed
to be in m/s) as user inputs, and print out the range R in metres. (You can set the
constant g=9.81 m sβ2 inside the program.) Use your program to investigate how R varies
with ΞΈ for a fixed value of v. At a given v, for what value of ΞΈ is R a maximum?
Keele University Physics/Astrophysics Laboratory Programming with Python Page 13
DECISIONS: ifβ¦else Get back the quadratic-equation program that you wrote and saved earlier (page 7). Run it again
to see what happens when you input values of the variables a, b and c that give two real roots for
x, versus complex roots (for example, try a, b, c = 1, 4, 2 versus a, b, c = 1, 2, 3).
That program is able to handle the case of complex roots because it imports the module cmath,
which contains built-in functions that are designed to work with complex numbers. However, it is
important to have some idea of how the functions in cmath deal with complex numbers.
Therefore, change the statement import cmath at the top of the program to import math instead,
so that you are forcing yourself to use built-in functions that can only work with real numbers
(either as arguments or as results). You then must also change the occurrence of the built-in
function cmath.sqrt() to math.sqrt() instead. Save the revised program (under a different nameβ¦
i.e., after editing the program in a scripting window, select the menu option βFile > Save Asβ¦β to
save it.) Now what happens if you try to solve a quadratic equation that does not have real roots?
To get around this problem while still using only real numbers, and functions only from the math
module (not cmath), modify your program so it appears exactly as follows:
# Program to solve a quadratic equation, ax^2 + bx + c = 0, with real roots import math # functions in the math module only work with real numbers a = input(" Please enter the number a: ") b = input(" Please enter the number b: ") c = input(" Please enter the number c: ") # a=float(a) b=float(b) c=float(c) disc = b**2 - 4*a*c if (disc >= 0): disc = math.sqrt(1.0*disc) x1 = (-b+disc)/(2.0*a) x2 = (-b-disc)/(2.0*a) print("\n" + "Solutions are: x = " + str(x1) + " and x = " + str(x2)) else: print("\n" + "Error. No real roots.") stop=input("\n" + "Press <Enter> to quit." + "\n")
Now try running it again with a set of a, b, c values that lead to complex roots. This is still not
entirely satisfactory, but at least the program doesnβt crash. On the other hand, try giving an input
Keele University Physics/Astrophysics Laboratory Programming with Python Page 14
value of a=0 to this program, along with any values at all of b and c. What happens in this case?
This needs to be fixed.
In the absence of any control structures, a program runs by starting at the first statement and
executing the following statements one-by-one until the end is reached. We have just seen an
example of a control structure, ifβ¦else. Inside parentheses following the word if is a logical
expression, disc >= 0, which evaluates to either True or False. If the result is True, then the
program executes the block of statements that immediately follow the if statement and are
indented by a fixed number of spaces from the beginning of the line. In this case, the else
statement and any block of uniformly indented statements immediately following it are ignored
altogether, and the program continues with the first unindented statement it finds after the else branch of the structure. If the logical expression in the if statement is False, then instead the
whole (indented) if branch of the structure is ignored, the statements indented after the else
statement are executed instead, and once those are done the program then continues on from
where the indenting stops. (The else branch may be omitted if it is not required, i.e., if you donβt
actually want to do anything special at all in the case that a particular logical expression is false.)
In Python, for an ifβ¦else control structure to work properly, the statements actually containing the
words if and else must not be indented; while the blocks of statements to actually be executed in
each of the two branches must be indented, and all by the same number of spaces. It is only
when this indenting stops that Python knows it is out of one ifβ¦else branch and into the next, or
out of the entire structure altogether and back to sequential statements that are executed in all
cases. When writing code in IDLE or in the associated scripting shell, the commands on lines
following if and else statements will be automatically indented for you, by a default, fixed number
of spaces. You need to stop this indentation yourself (backspace to delete the leading spaces that
automatically appear on a line) when you decide that any part of an ifβ¦else structure is complete.
Problem 1.2
Modify the program on page 13 so that it prints out the roots in the case where they are
complex. Use only built-in functions from the math module; do not import cmath. Since
the functions in math can only operate on real numbers, you will need to handle the real
and imaginary parts of complex roots separately.
Also, use another ifβ¦else control structure in this program to make it handle the case
a=0 correctly without crashing.
Test the final version of the new code thoroughly, and save it to your S: drive.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 15
LOOPS: for AND WHILE Another important control structure is the for loop. Try this program: # Program containing a for loop import math print("\n" + "Table of square roots" + "\n") for i in range(1, 10, 1): print(str(i) + " " + str(math.sqrt(1.0*i))) stop=input("\n" + "Press <Enter> to quit." + "\n")
The for loop here executes a block of statements repeatedly for successive values of the variable
i determined by the three arguments given to the built-in range() function. (This is another function
that is automatically available to all programs and does not require any module to be imported.)
The block of statements to be executed repeatedly as part of a loop are indented, by the same
number of spaces, exactly as for the branches of an ifβ¦else structure. The end of the block of
statements to be looped over, and a return to sequential statement execution (after the full range
of i has been gone through), is indicated by stopping the indentation. (In the small example here,
the βblockβ is just a single print(β¦) statement.)
Experiment with the arguments to range() in the program just above, to see what they all mean. In
particular, figure out how to change the step size between successive values of the loop variable i. Then modify the program so it uses a for loop to print a table of cube roots for all the odd integers
only between 1 and 11, including both 1 and 11. If you use the fact that the cube root is the 1/3
power of a number, then you need to remember that there is a difference between the quotients
(1/3) and (1.0/3.0) in Python.
In working some of these things out, you may find it helpful to look at another example as well:
# Another program with a for loop import math for degrees in range (0, 95, 5): radians = math.pi*degrees/180.0 print(str(degrees) + " " + str(radians) + " " + str(math.sin(radians))) stop=input("\n" + "Press <Enter> to quit." + "\n")
The for loop repeats a block of (properly indented) commands a specific, pre-determined number
of times. We can also have loops that repeat an initially undetermined number of times (even,
Keele University Physics/Astrophysics Laboratory Programming with Python Page 16
potentially, either indefinitely or not at all) depending on whether a logical expression evaluates as
true or false. This kind of repetition is done using a while loop. One case you might imagine,
which requires a while loop, is asking a user for some input and continuing to ask until the input
entered is equal to some particular, desired value. Here is an example of this:
# Program illustrating the use of a while loop name = "none" while ( (name != "stop") and (name != "STOP") ): name = input("What is your name? ") print ("Hello " + name + ".") stop=input("\n" + "Press <Enter> to quit." + "\n")
Again, the program starts to execute statements sequentially, so the first thing it does is assign the
value βnoneβ to a string variable called name. It then comes to the while statement, which tests to
make sure that the value of name is not equal to either βstopβ or βSTOPβ. If it is not, then the block
of uniformly indented statements immediately after the while are executedβthe user is asked to
input a new value for name, and then the program says helloβuntil the program encounters the
first unindented line (blank lines are always overlooked). It doesnβt execute the unindented line
(yet) but loops back to the while statement to test whether the new value of name is either βstopβ
or βSTOPβ. When the user finally enters one of these words as name, the logical expression in the
while statement will be false the next time it is executed. Then the program will stop looping, skip
over the indented statements in the while block, and resume simple, sequential execution of the
rest of the code from the first unindented line that follows. (In this example, the βrest of the codeβ is
just our usual, one-line βhangβ waiting to exit entirely.)
Actually, the program above does not quit in the most sensible way when it is finally given stop (or
STOP) as an input for name. We can take care of this by subtly changing the order of asking the
user for a name and testing that name in the while statement, and by exploiting the fact that
Python allows for while loops to contain else branches (as the ifβ¦else structure and, in fact, the
for loop also allow). Thus, try the next program now. Be sure you understand fully the different
behaviour when the loop is terminated in this program versus the previous one.
# Program illustrating the use of a whileβ¦else loop
name=input("What is your name? ") while ( (name != "stop") and (name != "STOP") ): print ("Hello " + name + ".") name=input("What is your name? ") else: print("Fine. Donβt tell me. ")
stop=input("\n" + "Press <Enter> to quit." + "\n")
Keele University Physics/Astrophysics Laboratory Programming with Python Page 17
PYTHON: WORKSHEET 2
Statistics and random numbers are important tools in computational physics and astrophysics,
with applications ranging from straightforward data analysis to the numerical approximation of
ideal mathematical expressions and the simulation of extremely complex physical systems. In this
Worksheet, you will generate sets of (pseudo)random numbers and develop algorithms using
them to compute basic statistics (mean and standard deviation) and to estimate the value of Ο.
First, we need to set out how to keep track of a running sum of a set of numbers. Thus, try the
following, which simply adds all the integers from 1 through 20:
# Algorithm for the running sum of a set of integers thesum = 0 for i in range(1, 21, 1): thesum = thesum + i print("The sum of integers from i=1 to i=" + str(i) + " is: " + str(thesum)) stop=input("\n" + "Press <Enter> to quit." + "\n")
Note a few important things here: First, we are storing the sum of the integers in a variable called
thesum. You might be tempted to call this variable just βsumβ, but there is actually a built-in
function in Python called sum() β which we canβt use in this program, because it does not do
quite what we want β and it is good practice in general to avoid possible confusion between your
own variable names and built-in elements of Python itself. Second, the program starts by setting
thesum=0. This initialises the variable. It is good practice always to explicitly assign a value to any
variable you want to have in a program, before you start using the variable in expressions or
modifying it in any way. Third, the built-in range() function, which you encountered already in
Worksheet 1, takes 21=20+1 as its second argument in order to make the counter variable i in the
for loop go up to and include the desired maximum of 20.
Next, let us see how to generate βrandomβ numbers. There are many built-in functions in Python
for working with random numbers. All of them are stored in the random module, which is
described in detail in Section 9.6 of the standard Library Reference in the Python Documentation.
Thus, we need to import random into any programs where we want to use random numbers.
(This module is distinct from the math module, so very often we still need to import math as well.)
With this in mind, the following short program will generate some user-specified quantity num of
random numbers x β all having values 0 β€ x < 1 β and also calculate the mean of these
numbers:
Keele University Physics/Astrophysics Laboratory Programming with Python Page 18
# Program to generate random numbers and calculate their mean import random num = input("\n" + "How many numbers do you want to generate? ") num = int(num) random.seed() thesum = 0.0 for i in range(1, int(num)+1, 1): x = random.random() thesum = thesum + x mean = thesum/(1.0*num) print("The mean of your numbers is: " + str(mean)) stop=input("\n" + "Press <Enter> to quit." + "\n")
This program makes use of two of the built-in functions in the random module. The first,
random.seed(), is used to initialise the random-number generator in Python. This raises some
technical issues about exactly how βrandomβ the numbers generated by computers really are.
Suffice it to say that they are better described as pseudo-random numbers, meaning (very
roughly) that they are generated in a deterministic (not unpredictable) way following the
specification of an initial value (the seed), which may be randomly set. The function
random.seed() specifies this seed value. If it is invoked without an argument, as in the program
just above, then Python uses the computerβs clock time to define the seed, and every time you run
the program it will generate a truly different sequence of numbers and give different results for
calculations using them. If you do give a (constant) argument to the seed function β for example,
you might write random.seed(8.7) β then every time you run the program it will generate the
same sequence of random numbers.
The function random.random() generates a single βrandomβ number between 0 (inclusive) and 1
(exclusive). The numbers generated by this function are uniformly distributed, i.e., there is an
equal probability of any one number having any value between 0 β€ x < 1. The random module
does contain other functions for generating numbers that fall between different limits and/or that
are distributed differently between the limits (e.g., following a Gaussian). You can learn more
about these alternatives in Section 9.6 of the Library Reference part of the Python Documentation.
Test the program above by inputting (for example) values of 1,000 then 10,000 then 1,000,000 for
the number num. Run the program several times for each value of num, to see the differences in
the means you get out each time. What do you expect the value of the mean to be in the limit of
arbitrarily large num?
Keele University Physics/Astrophysics Laboratory Programming with Python Page 19
Problem 2.1
The standard deviation of a set of numbers {π₯π₯ππ} is defined by
ππ = οΏ½β¨(π₯π₯ β β¨π₯π₯β©)2β©
where the notation β¨π΄π΄β© represents the mean of all the values of quantity A.
First, show algebraically that the definition of Ο can be re-written as
ππ = οΏ½β¨π₯π₯2β© β β¨π₯π₯β©2
This form is more appropriate for computer analysis and is the one you should use. Why?
Now modify the program above, which already calculates the mean of some user-
specified quantity of uniformly-distributed random numbers between 0 and 1, so that it will
also calculate (and print out) the standard deviation of the same set of numbers. To what values do the mean and the standard deviation tend, as you generate more and
more random numbers? Can you explain these limiting values for β¨xβ© and Ο?
You will need to import both the math module and the random module into your new
program, which you can do with a single statement: import math, random
Include as many comments as you see fit in your program, test it thoroughly as you go,
and be sure to save it to your S: drive when it is done.
Now, do the following problem:
Now you will use pairs of uniformly-distributed random numbers between 0 and 1 to obtain an
estimate for the value of Ο. The ideaβwhich is actually an example of a powerful and widely used
numerical method called Monte Carlo integrationβis as follows.
Suppose we generate two random numbers, say x and y, each distributed uniformly between
0 β€ x < 1 and 0 β€ y <1. If these were plotted together as a coordinate in the xy plane, they would
define a point somewhere in (or on the boundary of) a square with sides of length 1. If we were to
generate arbitrarily many such pairs of numbers, then eventually the points they define would fill in
this entire square. Moreover, each uniformly-distributed (x,y) point generated in this way would
have an equal probability of falling anywhere in the square, and so eventually they would fill in the
entire area evenly, i.e., the density of points would be the same everywhere.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 20
The square defined by 0 β€ x < 1 and 0 β€ y < 1 contains the (positive-x, positive-y) quadrant of a
circle of radius 1. If the area of the entire square becomes evenly filled in after randomly
generating a very large number of uniformly-distributed (x,y) points, then of course the quadrant of
the radius-1 circle inside the square also gets evenly filled inβas this figure illustrates:
In the left-hand panel here, 200 independent random numbers are put into pairs and plotted as
100 points in the xy plane; in the middle panel there are 500 randomly generated (x,y) points; in
the right-hand panel there are 2500 points. Green points have distances οΏ½x2+y2 β€ 1 from the
origin and so fall within the positive-(x,y) quadrant of the circle with radius 1 (the dashed curve).
The area of the circular quadrant in this figure is Ο/4 (one-quarter the area of a circle with radius
r = 1), while the area of the full square is just 1. Thus, the ratio of the areas is Ο/4. But when the
square is filled in evenly, this ratio of areas must equal the ratio of the number of points inside the
circular quadrant to the total number of points in the whole square. Since the even filling happens
as the number of points increases, we conclude that: If N points (x,y) are generated randomly,
with x and y independent of each other and both distributed uniformly between 0 and 1, then in the
limit of large N, the fraction of points having r β‘ οΏ½x2+y2 β€ 1 must tend to the value Ο/4.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 21
Problem 2.2
Once you have completely understood the argument just above, calculate the value of Ο
by Monte Carlo integration, using the random number generator random.random().
Let the number N of (x,y) points to generate be input to the program by the user, and
make the program output (1) the value of Ο estimated for the input N value; (2) the βtrueβ
value of Ο according to Python (i.e., the built-in constant math.pi); and (3) the
percentage relative error in your Monte Carlo estimate of Ο, which is defined by
100 Γ (your estimate β Ο) Γ· Ο.
Experiment to see how your numerical estimate for Ο depends on N. What N do you need
to reliably (i.e., repeatably) obtain a value for Ο that has a relative error of 1%? A relative
error of 0.1%? A relative error of 0.01%?
Keele University Physics/Astrophysics Laboratory Programming with Python Page 22
PYTHON: WORKSHEET 3 In this Worksheet, you will make use of what you have learned so far (especially about the while
loop) to investigate the problem of testing numerically for the convergence of infinite series. Then,
you will go on to learn some techniques for working with sets of numbers in Pythonβtechniques
that would be useful, for example, in the analysis of experimental data. You will learn about data
constructs called lists (which are similar to arrays in many other languages), how to do some
basic operations on lists, and how to write to and read from files on disc (data input/output, or I/O).
SERIES Infinite series come up surprisingly often in computational problemsβsuch as in the solutions to
the integrals of many functions, or in the evaluation of βspecialβ functions that donβt have closed
analytical forms. They also appear in the context of evaluating functions like exp(x) or
approximating the values of constants like Ο. It is therefore important to be able to test for the
convergence of series, and to approximate their sums to any desired accuracy with a finite number
of operations. For example, consider the convergent series
οΏ½ 1ππ2
β
ππ=1
= 1
12 +
122
+ 1
32 + β―
The following program sums the terms in this series until the terms become less than some
(small!) value specified by a variable called tolerance. It then stops, and the sum of the finite
number of terms to that point is taken as an approximation of the series sum to infinity.
# Program to approximate the sum to infinity of terms (1/k^2) tolerance = input("\n" + "Enter a value for the tolerance: ") tolerance = float(tolerance) k = 0 thesum = 0.0 nexterm = 1.0/((k+1)*(k+1)) while (nexterm >= tolerance): k = k + 1 thesum = thesum + nexterm nexterm = 1.0/((k+1)*(k+1)) print("\n" + "Terms are below tolerance for k > " + str(k)) print("Sum of terms to k = " + str(k) + " is: " + str(thesum)) stop=input("\n" + "Press <Enter> to quit." + "\n")
Keele University Physics/Astrophysics Laboratory Programming with Python Page 23
Study this algorithm closely and test the program using several values for the tolerance. By
specifying smaller and smaller tolerance and noting the associated change in the value of
thesum, obtain an estimate for the sum to infinity, Sβ , that is accurate to five significant figures.
Hence, what is the value of οΏ½6Sβ ?
This basic method of convergence testing can be used to tackle the following:
LISTS So far, you have been dealing with variables that are just single numbers or strings. However,
working only with individual objects can make it slightly cumbersome to determine even simple
facts about collections of numbers, such as their sum (recall Worksheet 2), or their minimum and
maximum. Consider the following program, which keeps track of the running sum, minimum and
maximum of a set of numbers input by the user:
Problem 3.1
Two infinite series that can be used to calculate Ο are
ππ8
= 1
(1)(3) +
1(5)(7)
+ 1
(9)(11) + β―
and
ππ4
= 1 β 13
+ 15
β 17
+ β―
Program both of these so that you can use the series sums to estimate the value of Ο.
(Figure out the general algebraic expression for the kth term in each of these series, and
then make appropriate modifications to the code on page 21.) Find which series
converges most rapidly. One approach is to do as above, and simply sum terms until the
absolute value of the terms drops below a tolerance. However, since you know the βtrueβ
value of Ο you can also keep adding terms until the absolute value of the relative error
between your estimate of Ο and the true value of Ο β that is, the absolute value of
(π¦π¦π¦π¦π¦π¦π¦π¦ ππππππππππππππππ β ππ) Γ· ππ β drops below a tolerance. What difference does this make?
The built-in function math.fabs(), which is in the math module, can be used to calculate
the absolute value of any real number. The value of Ο is in the built-in constant math.pi.
Remember to save your program to your S: drive.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 24
# A program for the running sum, minimum and maximum of user-entered numbers num = input("\n" + "How many numbers would you like to enter? ") thesum=0.0 for i in range (1, int(num)+1, 1): x = input("\n" + "Enter a number: ") x = float(x) thesum = thesum + x if (i == 1): minimum = x maximum = x if (x < minimum): minimum = x if (x > maximum): maximum = x print(" Current sum is " + str(thesum)) print(" Current minimum is " + str(minimum)) print(" Current maximum is " + str(maximum)) stop=input("\n" + "Press <Enter> to quit." + "\n")
Test this program thoroughly and make sure you fully understand the minimum/maximum
algorithm in particular.
If you type, for example, print(x) and print(minimum) in IDLE after one run of the program above,
you will see that these return just the most recent values of the variables. All prior information
about the other numbers you entered, and about the interim, running values of minimum (or those
of thesum and maximum), has been lost. This is obviously less than ideal.
A list in Python is similar to a construct called an array in other languages. It is a sequence of any
number of values (each of which may be numeric or alphabetic) collected and stored together in
memory so they can be manipulated or operated upon as a single unit. There are many built-in
functions designed specifically to work with lists. Among these functions are sum(), min() and
max(), which we can use to make the code above look significantly βcleanerβ while at the same
time retaining all information about all of the user-entered numbers.
You have, in fact, already used lists in these labs, every time you invoked the built-in range() function in the statement of a for loop. The statement range(n1, n2, nstep) actually creates the
list of integers [ n1, (n1 + nstep), (n1 + 2Γnstep), β¦ ] going up to but not including n2. Try:
z = range(1,10,1) print(z) z = range(20,1,-4) print(z)
Keele University Physics/Astrophysics Laboratory Programming with Python Page 25
As these examples show, a list is represented as individual values (the elements of the list)
separated by commas, all contained within square brackets, [ ]. You can use this notation to create
your own lists of constants or variables that are integers, real numbers, strings, or even mixtures
of these (though having a list that combines numbers and strings can make performing operations
on it somewhat trickyβnot recommended in general). For example:
z = [13, 3, -7.6, 18.2, 100] print(z) z = ["a", "b", "c", "d"] print(z) a = 34 b = "what" c = -7.45 d = "the?" z = [a, b, c, d] print(z)
You can make a list that contains a single element, if you wish, and you can concatenate any two
lists by using the β+β operator to join them directly. Thus:
u = range(1,10,1) print(u) v = [10] print(v) w = [ ] w.append(u) w.append(v) print(w) z = [ ] z.append(w) z.append(u) print(z)
Now revisit the problem above, of calculating the sum, minimum and maximum of some arbitrary
set of numbers. The numbers can be stored in a list as they are specified, and then you can use
the built-in functions sum(), min() and max() to work on the list in obvious ways. (All of these
functions are always available to any program and do not require a module to be imported.) Try
the following short code, which carries over to the top of the next page:
# A program to put user-entered numbers in a list and get the sum, minimum and maximum num = input("\n" + "How many numbers would you like to enter? ") for i in range (1, int(num)+1, 1): x = input("\n" + "Enter a number: ") x = float(x) if (i == 1): mylist = [x]
Keele University Physics/Astrophysics Laboratory Programming with Python Page 26
else: mylist = mylist + [x] thesum = sum(mylist) minimum = min(mylist) maximum = max(mylist) print("These are the numbers you entered: ") print(mylist) print("\n" + "The sum of your numbers is: " + str(thesum)) print("The minimum of your numbers is: " + str(minimum)) print("The maximum of your numbers is: " + str(maximum)) stop=input("\n" + "Press <Enter> to quit." + "\n")
It is worth noting that the functions sum(), min() and max() in fact consist of blocks of code that
work with the values of individual elements in the lists passed to them, in essentially the same way
as the for loops and if statements work in the program on page 23.
FILES AND DATA I/O
Python provides a variety of methods for working with both text and binary (machine-readable)
files. Here, you will look at just the most basic methods for writing and reading text files only; more
can be found in the official Python documentation.
The statement
myfile = open(filename, mode)
is used to open a text file that is called filename on disc (Python can create the file, if it doesnβt
already exist) and referenced as myfile in a Python code (in this sense, you can think of myfile as
a variable name, which you are free to choose subject to the usual rules). An access mode is
specified for the file: one of r for read, w for write or a for append. Both filename and mode must
be given to open(β¦) as strings (e.g., enclosed in quotation marks, " "). Again, once a file has
been opened, it is subsequently referenced in a code by the one-word name myfile to make
Python read or write or perform other operations on it. When you are done with the file, it can be
closed by the command myfile.close(). It is good practice always to close a file explicitly in this
way, rather than relying on either Python or your OS to clean up after you automatically.
By default, Python will try to open files from the folder C:/Python35. However, you can direct it to
other folders by including a full path in the argument filename given to open(β¦). For example, to
read from a text file called junk.txt, which is contained in a folder called my python stuff on your S:
Keele University Physics/Astrophysics Laboratory Programming with Python Page 27
drive, you would type myfile=open("S:/my python stuff/junk.txt","r"). To write to a file called
junk.txt on your own Desktop, you would enter myfile=open("S:/Desktop/junk.txt","w").
If you try to open a non-existent disc file for reading (access mode "r"), Python will give an error.
If you open a file for writing (access mode "w"), Python will create the file if it doesnβt already
exist. If the file does already exist on disc, then its old contents will be erased and the file will be
overwritten.
Opening a file for appending (access mode "a") will also create the file if it doesnβt already exist
(and then allow writing to it). But if the file does exist, it will be opened for writing with the old
contents preserved and anything new added to the end of the file.1
Now, open a file called junk.txt on your S: drive Desktop and write some lines of text to it, by
typing the following in IDLE. (Note that only strings can be written to or read from text files.)
myfile=open("S:/Desktop/junk.txt","w") myfile.write("Hello.") myfile.write("Goodbye.") myfile.write("Hello." + "\n") myfile.write("Goodbye again." + "\n") myfile.close()
Double-click on the file that has been created on your Desktop, so you can see the results of these
commands. In particular, notice that Python does not automatically put a break at the end of any
line that you write; you must do this yourself, by explicitly writing the string "\n" as needed.
You can write an entire list to a text file (if every element of the list is a string) by using the method
writelines() rather than simply write(). For example, do the following to append some text to the
file junk.txt that you created just above:
myfile=open("S:/Desktop/junk.txt","a") ss="Appended material following..." + "\n" # defining a string variable myfile.write(ss) # writing it to a file mylines=[ "Hello again." , "\n" , "Goodbye for good." , "\n" ] # defining a list of strings myfile.writelines(mylines) # writing them all to a file myfile.close()
Double-click on junk.txt on your Desktop again, to see how the appending worked. What would
have happened if the elements "\n" had not been included in the list mylines when you wrote it? 1 The access modes "r" and "w" or "a" are mutually exclusive, in that they allow either reading from or writing to a file, but not both
at the same time. There are other access modes, in which Python can simultaneously read from and write to a file. However, on
Windows platforms these modes can lead to technical complications that, although certainly manageable, are rather beyond the
point of these labs. Thus, donβt try the more sophisticated file access modes here.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 28
A file that is referenced as (e.g.) myfile in a Python code has various attributes associated with itβ
such as the actual name of the file on disc; the mode of access granted most recently to the file;
and whether it is currently closed or not. These can be queried directly and used as variables if
desired: myfile.name is a string containing the on-disc file name; myfile.mode gives the most
recent access mode as a string; and myfile.closed is a logical variable that is either True or
False. For example, just after your work above on the file junk.txt, type the following in IDLE:
print(myfile.name) print(myfile.mode) print(myfile.closed)
Python can read text files either character-by-character or line-by-line. Here, you will only be
reading entire lines; but either way, anything read in gets stored as strings. Returning to junk.txt:
myfile=open("S:/Desktop/junk.txt","r") theline=myfile.readline() print("First line in " + myfile.name + ": " + theline) theline=myfile.readline() print("Second line: " + theline)
Notice that after reading a line, Python stays positioned at the beginning of the next line. All lines
from the current position to the end of the file can be read in at once, and stored as the elements
in a list of strings, by using the method readlines() (plural) rather than readline() (singular). Thus:
therest=myfile.readlines() print(therest) # remaining lines in junk.txt, as a list of strings
At this point, you are at the end of the file and trying to read another line will return an empty
string. To get back to the top, you could close and then re-open the file; but more simply, just type
myfile.seek(0)
which tells Python to position itself zero bytes in from the beginning of the file.
Another way to read every line from the current position through to the end of a file, still using a
minimum of code but now having each line as a string separate from the others (rather than all
lines kept in a single list), is with a for loop:
for theline in myfile: print("Line read from " + myfile.name + ": " + theline) myfile.close()
Here, the for statement itself is equivalent to repeatedly executing theline=myfile.readline() in a
loop over the entire file and testing for the end of the file each time, in order to know when to stop.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 29
Numbers need to be converted to strings before they can be written to a text file. The built-in
function str() performs this type conversion, as the following few lines of code illustrate (typing
these in IDLE now will overwrite the contents of the file junk.txt on your Desktop):
myfile=open("S:/Desktop/junk.txt","w") for i in range(0,10,1): x=2*i+1 myfile.write(str(x)+"\n") myfile.close()
Notice the "\n" included in the argument to myfile.write(β¦). This is so that each number appears
on a separate line in junk.txt (which you should look at after doing this).
Conversely, to work with numerical data taken from a text file requires first reading the lines of the
file as strings and then converting those strings to numbers. If there is just one number on each
line in the original fileβas there is in junk.txt just writtenβthe built-in function float() can do the
necessary conversion from string to (real) number. In IDLE, type:
myfile=open("S:/Desktop/junk.txt","r") mylist=myfile.readlines() print(mylist) myfile.seek(0) for theline in myfile: x=float(theline) # converts string theline to a real variable called x, i=int((x-1.0)/2.0) # allowing normal mathematical operations on it print(str(i) + " " + str(x)) myfile.close()
Keele University Physics/Astrophysics Laboratory Programming with Python Page 30
Problem 3.2
Save and run the following program, which generates random numbers between 0 and 100 and writes them, one per line, to a file called Problem3.2.txt on your Desktop:
import random num = input("\n" + "How many random numbers would you like to generate? ") theseed = input("Enter a seed for the random-number generator: ") random.seed(theseed) myfile = open("S:/Desktop/Problem3.2.txt","w") for i in range (1, int(num)+1, 1): x = 100.0*random.random() myfile.write(str(x)+"\n") myfile.close() print("\n" + "Done. Output is contained in " + myfile.name) stop=input("\n" + "Press <Enter> to quit." + "\n")
Now add to this program. After the file Problem3.2.txt has been written,
β’ ask the user to enter any real number Y between 0 and 100; β’ read Problem3.2.txt, find all the entries in it that are greater than Y and report the
total number of these; and β’ calculate and report the minimum, the maximum and the mean of the values in the
file that are greater than Y.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 31
PYTHON: WORKSHEET 4 In this Worksheet you will learn about more properties of lists in Python, following on from the
basics introduced in Worksheet 3. You will also get to know the tryβ¦except control structure,
which provides a way for programs to deal smoothly with possible (anticipated) errors, rather than
crashing every time a simple problem is encountered. (In particular, you will manage the case of a
user who is prompted to input a number when running an interactive code, but who inputs a string
instead.) You will make use of these things to write a program that first identifies all the prime
numbers smaller than 1000, and then finds all of the prime-number factors of any integer, entered
by a user, in the range 2 β€ N β€ 1000.
MORE ABOUT LISTS Recall that lists in Python are contained within square brackets, [β¦], with the individual elements
of the list separated by commas. The elements themselves can be numerical (real or integer),
string, or logical variables or constants, or any mixture of these. Some or all of the elements of a
list can even be lists themselves, of any length.
Creating a list Just as simple variables in Python should be initialised before they are used in
any program, so too a list should be identified as such before attempting any operations on it. If
you know exactly what you want a list to contain, then you can simply type it in on a line of your
program. For example, try the following in an IDLE shell:
mylist = [1, 2, 3, 4] print(mylist)
Frequently, though, you will know that you want to store some variables or constants (numbers,
strings, whatever) in a list but you need to do some calculations to determine the values (or even,
possibly, to decide the total quantity) of the elements to go into the list. In such cases, you can first
create an empty list, which you will then be able to fill in and add to as a program goes on. This is
simply done:
mylist = [ ]
is empty.
Appending to a list Once a list exists, empty or otherwise, you can add elements to it as you
wish. Again, the elements donβt have to be all of the same type. Nor is there any need to know
beforehand how long you ultimately want the list to be; you can always add elements to the list (or
delete elements from it, or change the value of any element) at will.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 32
The correct way to append a new element to the end of a list is illustrated by the following, which
you should type into IDLE:
mylist = [ ] print(mylist) mylist.append(1) print(mylist) mylist.append("hello") print(mylist) mylist.append([2,3,4]) print(mylist)
Notice (from the first two lines of the above) that if you βappendβ to an empty list, you are really just
specifying the value of the first element of the (now non-empty) list. Any βappendsβ after that
increase the size of the list. Thus, after executing the few lines of code above, you will see that
mylist consists of three elements: the number 1, the string βhelloβ and the list [2,3,4].
There is an important difference between the append operation (which adds one element at a
time to the end of a list) and the + operator that was used in Worksheet 3 (which concatenates two
entire lists). To see the difference clearly, type the following in an IDLE shell immediately after the
lines above:
mylist = mylist + [5,6,7] print(mylist) mylist.append([5,6,7]) print(mylist)
When adding to a listβespecially if what you are adding is itself a listβalways think carefully
about whether you need to use append or + .
Length of a list The length of a list is, of course, the number of elements in it. (An empty list has
length 0.) The built-in function len() in Python will give you the current length of any list. Type the
following into IDLE:
mylist = [ ] L = len(mylist) print(L) mylist.append([1,2,3]) print(len(mylist)) mylist = mylist + [4,5,6] print(len(mylist))
Referencing elements in a list Each element in a list is indexed by an integer: 0 for the first
element, and (Lβ1) for the last element in a list of length L. You can use the indices to recover the
value of any single element in a list. Type the following in an IDLE shell to see how this works:
Keele University Physics/Astrophysics Laboratory Programming with Python Page 33
mylist = [1.0, 2.0, 3.0, 4.0, 5.0] x1 = mylist[0] print(x1) x2 = mylist[4] print(x2) x3 = (mylist[1] + mylist[3])/2.0 print(x3) x4 = mylist[5]
(The final command is supposed to give you an error!)
Very often we want to use the elements of a list, one after another, in a sequence of calculations.
This is easily done with the for loop in Python. Within IDLE, type
mylist = [1, 2, 3, 4, 5] L = len(mylist) for i in range(0,L,1): xx = mylist[ i ] + 1.5 print(xx)
More simply, the above is equivalent to the series of commands (try them),
mylist = [1, 2, 3, 4, 5] for j in mylist: xx = j + 1.5 print(xx)
Changing elements of a list Finally, it is possible to change, replace or delete any individual
element in a list:
mylist = [1.0, 2.0, 3.0, 4.0, 5.0] print(mylist) mylist[1] = (mylist[0] + mylist[3])/2.0 print(mylist) mylist[1] = "hah!" print(mylist) del mylist[1] print(mylist) print(mylist[1])
PRIME NUMBERS A prime number is an integer that is not evenly divisible by any other integer besides itself (and 1,
of course). As such, a number N is tested for βprime-nessβ by looking at the divisions of N by
smaller integers. If the remainder of N Γ· m is non-zero for every m less than N, then N is prime;
but if NΓ· m gives a remainder of 0 for any m, then m is a factor of N and N is clearly not prime.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 34
The built-in Python function math.fmod(x,y), which is contained in the math module (so programs
using it need to import math), returns the remainder of the division xy. Type the following in an
IDLE shell, to see how this function works:
import math r = math.fmod(19,2) print (r) r = math.fmod(28,7) print(r) r = math.fmod(43,13) print(r) r = math.fmod(13,43) print(r)
The following program uses math.fmod() and exploits some of the properties of lists described
above to find the prime-number factors (if any) of any integer N between 2 and 40. Enter it exactly
as written into a new scripting shell (click on βFile > New Windowβ in the menu at the top of an
interactive IDLE shell) and then save it to your S: drive (remember to include a .py extension on
the file name), so that you can run it repeatedly and modify it later, for the Exercise below.
# Program to find the prime factors of any integer 2 <= N <= 40 import math primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] getnumber = True while (getnumber): N = int(input("\n" + "Enter an integer between 2 and 40: ")) if ((N<2) or (N>40)): getnumber = False else: factors = [ ] for p in primes: if (p < N): remain = int(math.fmod(N,p)) if (remain == 0): factors.append(p) if (len(factors) == 0): print(str(N) + " is a prime number.") else: print(str(N) + " has prime factors " + str(factors) + ".") stop=input("\n" + "Invalid input. Press <Enter> to quit." + "\n")
Keele University Physics/Astrophysics Laboratory Programming with Python Page 35
This code combines several things that you have come across in this and previous Worksheets.
The logic behind it is as follows:
After importing the math module, the program creates the list primes, which contains all of the
prime numbers smaller than 40 (note that 1 is excluded, since it is trivially a factor of any number).
Then it defines a logical variable, getnumber, and initialises its value to True (note that the capital
T is required for True to be understood by Python as a logical value). The rest of the program
consists of a single while loop, which is executed over and over for as long as getnumber still has
the value True. Inside the loop, a user is asked to enter a number, which gets stored as an integer
in the variable N. If this is outside the range 2 β€ N β€ 40, then the logical getnumber is set to False
(the F must be capitalised), so the program will break out of the while loop and go to the very last
line of the code, waiting for the user to press the <Enter> key in order to exit completely. But if the
user enters a number that is between 2 and 40, then the program looks for all the prime-number
factors of N. To do this, it first creates an empty list that will be used to hold all the factors. Then it
goes one-by-one through the list of prime numbers smaller than 40. For each prime p that is
smaller than N, it finds the remainder of Np. If this is 0, then p is appended to the factors list for
N. After working through all the primes, if factors is still empty, then N is reported to be a prime
number; otherwise, the list of factors is printed to the screen.
Notice that this algorithm uses the fact that, to determine if N is prime, we only need to check
whether or not the prime numbers (not all integers) smaller than N divide into it evenly. (Think
about this, if it is not immediately obvious to you.) Also, N is not to be recorded as its own factor.
THE tryβ¦except STRUCTURE In running and testing this program, you may well have come across an aesthetic βflawβ: if, when
asked to enter a number to be factorised, you instead enter a string (or if you just press the
<Enter> key without typing anything at all), the program crashes. This doesnβt hurt anything or
invalidate any calculations, but it is avoidable behaviour that can be handled using the
tryβ¦except control structure in Python. This has the general form:
try: a command that could potentially cause an error (technically: raise an exception) except: block of code to execute if the command under try does raise an exception else: block of code to execute if the command under try does not raise an exception
Keele University Physics/Astrophysics Laboratory Programming with Python Page 36
Using the above as a guide, implement a tryβ¦exceptβ¦else structure in the prime-factorisation
code to make it work such that: If the user enters anything other than a single number in response
to the input command (i.e., if an error is raised), then the variable getnumber is simply set to
False so the while loop is exited smoothly, without crashing. The block of code that does the
actual factorisation should be executed only if the user properly enters a single number at the
input command so no error is raised. Save your modified code to your S: drive, test it thoroughly,
and then modify it further to solve the following problem.
Problem 4.1
Write a program to find and print out a complete list of all prime numbers smaller than
1000. After the full list of primes has been determined and printed, have the program ask
the user to enter an integer between 2 and 1000. If the input is valid, find the prime
factors of the number and print those factors to the screen. Continue asking for input and
reporting factors (or prime-number status) until the user enters either non-numerical input
or a number outside the range 2 β€ N β€ 1000βupon which, the program should exit
smoothly without crashing. Output from the program to the screen should look something
like this:
There are (print total) prime numbers between 2 and 1000. They are: [ β¦ (print list of prime numbers to the screen) β¦ ] Enter a number between 2 and 1000: 11 11 is a prime number. Enter a number between 2 and 1000: 90 90 has prime factors [2, 3, 5]. Enter a number between 2 and 1000: asdf Invalid input. Press <Enter> to quit.
Strategy for finding the prime numbers:
Remember that to determine whether or not a number N is prime, it is enough to test its
divisibility by just the prime numbers p < N. Thus, to find the primes smaller than 1000,
work one by one up through the integers N β₯ 2, checking whether each can be divided
evenly by any already-known primes p < N. If there is no prime p that divides N exactly,
then append N to the list of primes and go on to the next integer. Repeat until you reach
N = 1000.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 37
PYTHON: WORKSHEET 5 You have made use of many different built-in Python functions up to this point. In this Worksheet,
you will learn how to define your own functionsβpieces of code that contain calculations or an
algorithm that may need to be carried out repeatedly as part of larger programs, and as such are
worth βsplitting offβ and structuring so that they can be invoked with a single command (as the
standard, built-in functions are). You will make a user-defined function to work inside a program
that implements the bisection method to find the roots of algebraic equations.
USER-DEFINED FUNCTIONS Defining your own functions is straightforward, as the following program illustrates. Enter this in a
scripting shell (opened by clicking on βFile > New Windowβ in the menu at the top of an interactive
IDLE shell) so that you can save it (on your S: drive, with a .py extension on the file name) and
run/test it several times with different inputs.
# Program to convert temperatures between Celsius and Fahrenheit def cels(temp): # to convert 'temp' from Fahrenheit to Celsius temp = float(temp) degC = 5.0*(1.0*temp - 32.0)/9.0 return degC def fahr(temp): # to convert 'temp' from Celsius to Fahrenheit temp = float(temp) degF = 9.0*temp/5.0 + 32.0 return degF T = input("\n" + "Enter a temperature in degrees Celsius: ") print("In Fahrenheit, this is " + str(fahr(T)) + " degrees.") T = input("\n" + "Enter a temperature in degrees Fahrenheit: ") print("In Celsius, this is " + str(cels(T)) + " degrees.") stop=input("\n" + "Press <Enter> to quit." + "\n")
The user-defined functions here are, of course, cels and fahr. The keyword def signals that the
block of code to come on the lines following (which must be set off by consistent indentation)
constitutes a function. Stopping the indentation some lines after the def statement signals the end
of the function definition. The names of functions are followed immediately with parentheses,
which enclose the name(s) of any variable(s) that must be passed in to the function from the main
part of the program using it. (The only such argument required by cels and fahr here is temp.)
The code making up a function performs calculations using any argument(s) passed in and stores
Keele University Physics/Astrophysics Laboratory Programming with Python Page 38
the end result(s) in one or more variables (degC or degF in the above). There has to be a line to
return the final value(s) of interest to the main part of the program.
The main part of the simple program above is just the few lines that ask for some input from a user
and then print the results of CelsiusβFahrenheit conversions to the screen. In this example, the
only time that either function cels or fahr is invoked (or called) is inside a print statement; but in
general, functions can be called anywhere in a program, just as built-in Python functions can be.
(The only restriction is that a function must actually be defined before any attempt to use it in a
program.) The syntax for calling a user-defined function is also the same as that for built-in
functions: FunctionName(arguments). Notice that the argument that gets passed in to a function
need not (and most often will not) have the same name in the main part of a program as it has
inside the function itself. Thus, the value of the user-input variable called T in the main program
above gets transferred to the value of a variable called temp inside either cels or fahr.
THE BISECTION METHOD The bisection method can be used to find the root of any continuous function of π₯π₯βthat is, a value
of π₯π₯ at which ππ(π₯π₯) = 0βgiven two values of π₯π₯ that are known to bracket the root. This is
particularly useful as a way to solve algebraic equations numerically that cannot be solved
analytically. The method is iterative and works as follows:
Suppose we know that ππ(π₯π₯1) < 0 and ππ(π₯π₯2) > 0 (or vice versa) for some function ππ(π₯π₯) and
some π₯π₯1 β π₯π₯2. Then we know (assuming that the function is continuous) that a root of ππ lies
between π₯π₯1 and π₯π₯2. So, we try the mid-point
π₯π₯ππ = 12
(π₯π₯1 + π₯π₯2)
and evaluate the function there. We may find that ππ(π₯π₯ππ) = 0 and be done: π₯π₯ππ is a root. In
general, though, it is much more likely that we will find either ππ(π₯π₯ππ) < 0 or ππ(π₯π₯ππ) > 0. In this
case, ππ(π₯π₯ππ) will have the same sign as either ππ(π₯π₯1) or ππ(π₯π₯2).
If ππ(π₯π₯ππ) has the same sign as ππ(π₯π₯1) β and, hence, the opposite sign to ππ(π₯π₯2) β then we infer
that a root of the function must actually lie between π₯π₯ππ and π₯π₯2; thus, we define a new (smaller)
interval bracketing the root weβre after, by replacing the original π₯π₯1 with the midpoint π₯π₯ππ. However,
if ππ(π₯π₯ππ) has the same sign as ππ(π₯π₯2) then instead we know that a root must lie between π₯π₯1 and
π₯π₯ππ; thus, we define a smaller bracketing interval by replacing the original π₯π₯2 with the value of π₯π₯ππ.
Either way, we then calculate the midpoint of the new interval (π₯π₯1, π₯π₯2) and find the value of the
function ππ at this new midpoint in order to make a still smaller interval containing the root. We
continue on in this way until either we stumble across the value of π₯π₯ that gives ππ(π₯π₯) = 0 exactly,
Keele University Physics/Astrophysics Laboratory Programming with Python Page 39
or (much more likely) the size of the interval (i.e., |π₯π₯1 β π₯π₯2|) becomes smaller than a pre-set
tolerance so the root lying in the interval is constrained to a high precision.
The following is an algorithm (not a complete program, so donβt try to run it yet) for implementing
the bisection method. It is set up to find the root of a function of that is called simply f (more code
is needed to define the function), assuming that values x1 and x2 are already known, which
bracket a root of the functionβthat is, either f(x1) < 0 and f(x2) > 0, or f(x1) > 0 and f(x2) < 0.
# Algorithm for the bisection method (must import math before this can be used) tol = 1.0e-10 # 'tol' is the desired precision of the root of function f gotroot = False while (not gotroot): xmid = (x1+x2)/2.0 if ( f(xmid) == 0.0 ): xroot = xmid gotroot = True else: if ( f(x1)*f(xmid) > 0.0 ): x1 = xmid if ( f(xmid)*f(x2) > 0.0 ): x2 = xmid if (math.fabs(x2-x1) < tol): # math.fabs() is a built-in function in the math module xroot = (x1+x2)/2.0 gotroot = True if (gotroot): print("\n" + "Estimated root is x = " + str(xroot) + ", at which f = " + str(f(xroot)) + ".") stop=input("\n" + "Press <Enter> to quit." + "\n")
Read through this algorithm carefully, studying it with the basic principles of the bisection method
in mind, until you are confident that you understand what each line does, and why. Does the use
of the logical variable gotroot make sense to you? The while loop and each of the ifβ¦else
structures inside it? Why look at the signs of the products f(x1)*f(xmid) and f(xmid)*f(x2)?
A complete program that implements this algorithm has to define a function f and specify some
initial numbers x1 and x2 that bracket a root of the function. For example, the βlead-inβ to find the
roots of ππ(π₯π₯) = π₯π₯2 β 1 = 0 could be (it continues to the top of the next page):
# Start of a program to find roots of f(x) with the bisection method import math def f(xin):
Keele University Physics/Astrophysics Laboratory Programming with Python Page 40
xin = float(xin) # makes sure that xin is a real number (not an integer) f = xin**2 - 1.0 return f x1 = input("\n" + "Enter x1 bracketing a root of your function: ") x2 = input("\n" + "Enter x2 bracketing a root of your function: ") x1 = float(x1) x2 = float(x2)
Combine these lines with the code above for the bisection algorithm itself to make a complete
program, which you should save as a .py file on your S: drive. Then run it to make sure that it can
correctly find the two roots of π₯π₯2 β 1 (which, of course, you know to be π₯π₯ = 1 and π₯π₯ = β1).
For this program to work properly, the values π₯π₯1 and π₯π₯2 that you input must bracket a root of the
function; that is, one of ππ(π₯π₯1) or ππ(π₯π₯2) must be negative, and the other positive. What happens if
this is not the case? For example, try entering π₯π₯1, π₯π₯2 = 2, 3 (both values larger than the largest
root); or π₯π₯1, π₯π₯2 = β3,β2 (both smaller than the smallest root); or π₯π₯1, π₯π₯2 = β0.5, 0.5 (both in
between the two roots). Even more frustrating, try π₯π₯1, π₯π₯2 = β1, 1 (both values already roots).
There is still one slightly awkward issue, which cannot be fixed but is a fundamental limitation of
the bisection method for root-finding. Using your program as just modified in Problem 5.1, try and
find the roots of π₯π₯2 β 1 with initial guesses for bracketing values of π₯π₯1, π₯π₯2 = β2, 3. You already
know that this interval contains both of the roots of this function (at π₯π₯ = β1 and at π₯π₯ = 1), but what
happens when you run your program? Sketching the function should help you understand exactly
why this happens. It illustrates the more general fact that the bisection method cannot be relied
Problem 5.1
Modify the bisection program so that it recognises these kinds of potential difficulties and
can try to deal with them in a general way, rather than either crashing or going on to look
for a root in an interval where there is none.
Test the values of ππ(π₯π₯1) and ππ(π₯π₯2) as soon as π₯π₯1 and π₯π₯2 are entered. If one of π₯π₯1 or π₯π₯2 (or
both) is actually a root of the function, then have the program report this and exit
smoothly. If, instead, the values of ππ(π₯π₯1) and ππ(π₯π₯2) are either both positive or both
negative, then have your program report this fact and ask you (rather than just giving up)
to enter new guesses for values of π₯π₯1 and π₯π₯2 that might bracket a root.
Test this program by using it to find the roots of π₯π₯2 β 1 by starting with initial guesses of
π₯π₯1, π₯π₯2 = 2, 3; then π₯π₯1, π₯π₯2 = β2,β3; then π₯π₯1, π₯π₯2 = β1, 1. Make sure it behaves in each
case as it is supposed to, and save it to your S: drive, before continuing.
Keele University Physics/Astrophysics Laboratory Programming with Python Page 41
upon to find any roots of a function within a bracketing interval (π₯π₯1, π₯π₯2) that contains any even
number (including zero) of roots.
On the other hand, if the bisection method starts from a bracketing interval (π₯π₯1, π₯π₯2) that contains
any odd number of roots of a function, then it will always be able to find the value of one root in
that interval; but it will give you no idea about the values, or even the possible existence, of any
other roots in the same interval. To see this, use your code from Problem 5.1 to find roots of
ππ(π₯π₯) = sin(π₯π₯) = 0 .
(Recall that the built-in function math.sin(x) takes the argument π₯π₯ in radians.) Of course, the roots
here are at π₯π₯ = 0, Β±ππ, Β±2ππ, Β±3ππ, β¦ . Thus, run the bisection program with initial guesses for the
root-bracketing π₯π₯-values of: π₯π₯1, π₯π₯2 = β1, 1, then π₯π₯1, π₯π₯2 = β1, 4, then π₯π₯1, π₯π₯2 = β1, 7, and finally
π₯π₯1, π₯π₯2 = β0.5, 7.5. Take close note of the result you get in each case, and be sure you understand
why you get these results. Again, sketching the function will help.
The main lesson here is thatβdepending on the function it is applied toβthe success or failure of
the bisection method (and the value of any root it might find) can be very sensitive to the starting
guess for the bracketing interval (π₯π₯1, π₯π₯2).
Nevertheless, as long as the shortcomings just outlined are kept in mind, the bisection method can
still be very useful. Thus, run the program you developed in Problem 5.1 to solve the following:
Problem 5.2
(a) Find the roots of the transcendental equation
ππ(π₯π₯) = πππ₯π₯ β 3π₯π₯ = 0 .
There are two roots, neither of which can be determined analytically (that is, there is no
way to rearrange the equation to isolate π₯π₯ and set it equal to a numberβtry it!). Search
around until you have found both roots. Recall that the built-in function math.exp(x) can
be used to evaluate πππ₯π₯.
(b) Find the solutions of the equation
π₯π₯4 + π₯π₯3 + π₯π₯2 + π₯π₯ = 1 .
How many solutions are there in total? Is this what you expected? Why, or why not?