Omigosh: Happy New Year

Jarring chord. The door flies open and Cardinal Ximinez of Spain enters, flanked by two junior cardinals. Cardinal Biggles has goggles pushed over his forehead. Cardinal Fang is just Cardinal Fang

Ximinez: Nobody expects the Spanish Inquisition! Our chief weapon is surprise…surprise and fear…fear and surprise…. our two weapons are fear and surprise…and ruthless efficiency…. Our three weapons are fear, surprise, and ruthless efficiency…and an almost fanatical devotion to the Pope…. Our four…no… amongst our weapons…. amongst our weaponry…are such elements as fear, surprise…. I’ll come in again. (exit and exeunt)

Well, I guess you didn’t expect that…

The end of our teaching year fell upon us somewhat abruptly last year, with the final tutorial on 25 October.  We are now in a new year and have to make a start on some new tutorials.  This tute is just to take stock of where we were up to, what we were doing and why we were doing it.

Let’s refresh our memory on what we’ve covered recently.  This is what’s happened in the last few tutorials:

Ministry of Silly Objects: About objects, with some examples of methods and attributes.  In particular, we used a file object which was created by the open() function.  Methods are functions which are bundled with the object, while attributes are data which are bundled with it.  In the tutorial, for example, the object  fileObject had an attribute called name.  The name attribute was accessible by putting a dot between object and attribute, thus: fileObject.name.(<- this last dot is a full stop, it’s not part of the Python and if you are observant you will see it is not italicised)  Similarly fileObject had a method called close() which closed the file when you’re finished with it.  That method was accessed by fileObject.close().

It turns out that everything in Python is an object.  We saw that what we thought was data, for example a string, can have methods.  This, for example, shows a string method (try it yourself):

"This will be turned into uppercase".upper()

In this tutorial we also built a little on the filing tutorial (see next) in that we saw that files on the hard disk are accessed through file objects that Python creates.  The file objects allow you to do things like read from and write to the file.

Filing: We learnt a little about files.  Files are used to store data on the computer (usually on the hard drive).   Now that we know about files, we can store data so that we don’t have to keep typing it in every tutorial.  In particular, we were working on a trivia game, which means there will be a heap of questions and possible answers in it.  We will be using files to store these questions and answers between sessions.  The storage of data into a file is also called “serialization” (the data is “serialized”)  or persistence (the data is “persisted”).  In truth, serialization is a way of conditioning data so that it can be persisted.

Trivial Lists: We learnt about lists in this tutorial, with the view to using a list to store a question and the answers to that question.   If you remember, we stored each question in a separate list, with the wording of the question being entry 0, the correct answer being entry 1 in the list, and with a number of incorrect answers making up the other entries in the list.  This approach had one problem that we identified – the person playing the game could always just choose the first answer and would always get it right (since we stored the right answer first).

Trivia Game (Part 2): In this tutorial we  made use of the randint() function in the random package to randomise the location of the correct answer.  This overcomes the problem we found in the previous tutorial from the way we stored questions.   In this tutorial we also created a function called askQuestion() which took a question list as an argument. and asked the question, including randomising the position of the correct answer.  This way, if we have a question, we can simply pass it off to the function to have it asked.

Foundations: The Ministry of Silly Objects

DENNIS: What I object to is that you automatically treat me like an inferior!
ARTHUR: Well, I am king!
(Scene 3)

This post is about “objects”.   Not everyday sort of objects that you can touch.  Programming objects.  In fact, not any old programming objects, but Python objects.   Objects are a way of packaging together data and ways of dealing with the data.   In the previous tutorial we encountered a variable called fileObject – from last tute:

... fileObject = open(nameOfFile,'w')
>>> # fileObject is a file object
... # the open() function creates a file
... # and then
... # it opens the file for writing - 'w'
... # if the file already exists it just opens it

>>> # see, now it exists
>>> # let's write something to it:
... testMessage='This is a test message'
>>>
>>> fileObject.write(testMessage)
>>> # after we've finished with a file we should close() it ... fileObject.close()

The variable fileObject stores an object in much the same way as other variables store data – that is, they point to the object.  There is nothing special about the name, I just used it to call out the fact that an object was being stored in it.    What I want to draw attention to is that we created fileObject by calling a function – open().  However, we didn’t close it by calling another function close().   Rather, we closed it another way:

... fileObject.close()

The object connected to a function (actually called a “method” in this context) using a dot.  What has happened here is that the open() function, when it ran, returned an object.  Part of that object was a method called .close().  Indeed, if you look closely, there are other methods called .write() and .read():

fileObject.write()
          ^
          |----- Can you see the dot?
fileObject.read()
          ^
fileObject.close()
          ^

Methods allow programmers to keep functions and data which they are related to together in one spot and pass them around together like a parcel.  Just as functions which are kept in the object have a special name (methods), so too the data which is kept in the object have a special name – attributes.  So, revisiting the last tutorial (with a little twist because the file was already there – otherwise redo the last tutorial to create it), try this:

>>> import os.path
>>> nameOfFile = 'python4kidsTest.txt'
>>> os.path.exists(nameOfFile)
True
>>> fileObject=open(nameOfFile,'r')
>>> fileObject.name
'python4kidsTest.txt'
>>> fileObject.mode
'r'

Here the object stored in the variable fileObject has two attributes: name and mode.   We provided the data which is stored in these attributes when we called the open() function.  The data stored in these attributes are accessible by dotting them with the name of the object:

fileObject.name
fileObject.mode

I can demonstrate that these are specific to the object by opening another file and having a look at that object:

>>> fileObject2 = open('python4kidsTest2.txt','w')
>>> fileObject2.name
'python4kidsTest2.txt'
>>> fileObject2.mode
'w'

So, even though the attribute is called name, because it is part of an object it stores a data which is specific to that object:  fileObject.name is different from fileObject2.name.  Similarly for the mode attribute of each of these objects.

Objects have a heap of advantages, not all of which are easily explained at the moment. It suffices to say, though, that because they allow packaging of data and functions together, they allow you make programming heaps heaps easier.

Clean up:

fileObject.close()
fileObject2.close()

Confusing bit:

It turns out that everything in Python is an object (which is part of the wonder of Python), even strings:

>>> 'this is a string'
'this is a string'
>>> "it is also an object, see its upper method? ->".upper()
'IT IS ALSO AN OBJECT, SEE ITS UPPER METHOD? ->'
>>>

Keep reading that example until it seems at least a little bit freaky to you.

Homework:

(Subject to the previous observation that everything is an object…) which of the following are functions, which are variables, which are objects, which are attributes (which are constants/literals) and which are methods:

a = 1

b = open(‘testfile.txt’,’r’)

os.path.exists(b.name)

b.close()

Have a look at the earlier tutorials to see where the methods of objects have been used (do you remember .split() and .join()?).

Filing

Mr Chigger     Excuse me, I saw your advertisement for flying lessons and I’d like to make an application.
Secretary     Appointment?
Mr Chigger     Yes, yes.
Secretary     Certainly. Would you come this way, please.
She gets up, clutching a file and trips off in a typical efficient secretary’s walk. Mr Chigger follows. Cut to a river. She goes straight in without looking to right or left, as if she does this routine as a matter of course. Mr Chigger follows. Halfway across the river they pass a couple of business executives hurrying in the opposite direction.

We come now to a crossroads.   We can’t keep typing all the questions in each time.  That would be too tedious!  So, we’re going to look at one way of “persisting” data.  That is, storing data from one session so that you can use it in another session.   We’re not going to cover the way of storing Python objects (since we haven’t really looked at objects yet), rather, we’re going to do some filing – with files on the filesystem.  This makes use of the open() function.

>>> import os.path
>>> nameOfFile ='python4kidsTest.txt'
>>> # just a test filename
... # check to see if the file is there
...
>>> os.path.exists(nameOfFile)
False
>>> # it's not there, so we can do this:
... fileObject = open(nameOfFile,'w')
>>> # fileObject is a file object
... # the open() function creates a file
... # with a name nameOfFile (not "nameOfFile")
... # (don't go further until you understand the previous line)
... # and then
... # it opens the file for writing - 'w'
... # if the file already exists it just opens it
... #
... os.path.exists(nameOfFile)
True
>>> # see, now it exists
>>> # let's write something to it:
... testMessage='This is a test message'
>>>
>>> fileObject.write(testMessage)

Finally, we should close the file:

>>> # after we've finished with a file we should close() it
... fileObject.close()

If you have access to the file system somehow, you can verify for yourself that the file is there. To find out what directory you are in type:

os.path.abspath('.')

And it will tell you (‘.’ is shorthand for the current directory).  From there you can look for a file called “python4kidsTest.txt” and open it to see that it really does include the test message we wrote (try “cat python4kidsTest.txt” from a command line on Linux, or use explorer if you are on some other operating system).  You can do it from Python by using os.listdir(‘.’) – try it.

If you aren’t brave enough to do that, you can try it yourself from Python by opening the file and reading from it:

>>> fileObject = open(nameOfFile,'r')
>>> # now we open the file for reading - 'r'
...
>>> fileContents = fileObject.read()
>>> # this reads the contents of the file
... # and stores it in fileContents
...
>>> fileContents
'This is a test message'
>>> fileObject.close()

Now, let’s do it again, this time with feeling:

>>> fileObject = open(nameOfFile,'w')
>>> # note 'w' for Writing (ie sending data *to* the file)
... # this overwrites the file!
...
>>> fileObject.write('A new message\n')
>>>
>>> fileObject.close()
>>>
>>> fileObject= open(nameOfFile,'r')
>>> print fileObject.read()
A new message
>>> # you don't need to store it in a variable first
... fileObject.close()
>>>

Can you see that when we opened the file for writing again, it wrote over the original message we had in there?  If you’d rather append or add to what is there, you can use the ‘a’ file mode:

>>> # to add stuff to the file we use 'a':
... fileObject= open(nameOfFile,'a')
>>>
>>> for i in range(10):
...     newMessage='Writing another line, number '+str(i)+'\n'
...     fileObject.write(newMessage)
...
>>> fileObject.close()
>>>
>>> fileObject = open(nameOfFile,'r')
>>> # note 'r' for Reading (ie getting data *from* the file)
... print fileObject.read()
A new message
Writing another line, number 0
Writing another line, number 1
Writing another line, number 2
Writing another line, number 3
Writing another line, number 4
Writing another line, number 5
Writing another line, number 6
Writing another line, number 7
Writing another line, number 8
Writing another line, number 9
>>> fileObject.close()

See that the previous data we’d written to the file (‘a new message\n’) was still there when we read it?

Exercise:

Choose a file name, assign it to a variable called nameOfFile.  Use os.path.exists to see if it exists.  If so, choose another name, otherwise, use open to open the file and write a message of your choosing to it, then close the file, open it for reading, read the contents back then close the file.

Note

Often programmers use a shorthand for both the file object and the file name.  It is not unusual, for example to see something like:

FN=’somefile.txt’

f=open(FN,’w’)

f.close()

End Note:

Ooops! Did you see I slipped up? I said we weren’t worrying about objects, and then the very next thing I told you that fileObject was, well, a file object.  Maybe I have to bite the bullet and next give you a gentle introduction to objects…

Trivia Game (Part 2)

ARTHUR: He is the keeper of the Bridge of Death. He asks each traveler five questions–
GALAHAD: Three questions.
ARTHUR: Three questions. He who answers the five questions–
GALAHAD: Three questions.
ARTHUR: Three questions may cross in safety.
ROBIN: What if you get a question wrong?
ARTHUR: Then you are cast into the Gorge of Eternal Peril.
(At scene 23)

In the previous tutorial we defined a function to ask a question.  The question was sent to the function as an argument and had the type list.  The question itself was stored in index number 0 of the list (that is, the first entry) and the correct answer was stored at index number 1 of the list.   The homework for the last tutorial was to work out why this was bad.  Did you work it out?

The problem with this is that, the correct answer is always at the same place.  So if someone was playing the game, they would just answer ‘0’ all the time and always get the right answer.  So, we’re going to need to find some way of randomising the answers.   However, when we randomise them we also need to remember which is the correct one.

It turns out that there’s already a function which will do some of this work for us.  First we need to import the random module.

>>> import random

The random module has a function called randint() (which someone else, sometime ago kindly has already written for us, so we get it for free, in a sense).  Calling random.randint(a,b) returns a random integer greater than or equal to a and less than or equal to b:

>>> random.randint(1,4)                                
3                                                      
>>> random.randint(1,4)                                
3                                                      
>>> random.randint(1,4)                                
2                                                      
>>> random.randint(1,4)                                
4

From here, it is not that hard to modify the function so that it puts the right answer in a random spot.

>>> def askQuestion(questionList):   
...      question = questionList[0]  
...      answers = questionList[1:]  
...      numberOfAnswers = len(answers)-1 
...      # get the number of answers
...      # the "-1" is there because the list starts from 0           
...      # so the first entry is at 0 (that is, 1-1),
...      # so the last entry is at
...      # the total number of entries minus one.
...      correctAnswer = random.randint(0,numberOfAnswers)
...      # choose one at random
...      # now swap the random one for the correct answer
...      spam = answers[correctAnswer]
...      answers[correctAnswer]=answers[0]
...      answers[0] = spam
...      print question
...      for i in range(len(answers)):
...          print i,'. ',answers[i]
...      answer = raw_input('Enter number of correct answer: ')
...      if answer == str(correctAnswer):
...          print 'Correct!'
...      else:
...          print 'Wrong'
...

In this print out I’ve also included some commentary after the character #.  When Python finds a # it ignores everything which comes after it (these are called, unsurprisingly, “comments”).  In these tutorials you don’t need to type in comments, they’re there to explain why things are happening the way they are.  However, including comments in your code is very important because it helps you remember why you did what you did should you come back to your program at some later time.

The structure has changed a little bit from the previous tutorial.  This time, we’ve split the original question list into a question and a shorter list of answers.

Exercise: go back to the strings tutorial and look at the [:] operator.  See how it can be used on any list?

From there the comments explain what is happening.  We choose a random number – but one which is less than or equal to the number of answers.   Then we swap the correct answer (which is in answers[0]) for this one.  We know that it is in answers[0] because it used to be in item 1 of questionList[], but answers[] is everything from item 1 of questionList to the end of the list.   So, it becomes the first entry, which has an index of 0 – you need to get used to the first entry being at 0 stuff.

We have used a temporary variable called spam to swap the values.  We’ve called it “spam”, but any other variable name would have been acceptable.  In Python programs “spam” is sometimes used as a name for a disposable variable – that is, one which we’re going to use for a short time and then won’t care about.  For why, read this.

Exercise: why won’t this work:

answers[correctAnswer] = answers[0]
answers[0] = answers[correctAnswer]

(aside: Python has a special syntax to allow swapping values of variables, but we haven’t used it here).

Finally, we’ve changed the test to see whether the answer is correctAnswer (that is the random number we chose, and therefore the location to which the correct answer has been moved), rather than ‘0’ which it was before.

Exercise: What else have we done when checking whether answer is equal to correctAnswer?  Why?

So, let’s test this on a sample question:

>>> for i in range(4):
...     askQuestion(question1)
...
What colour is Jango Fett's armour?
0 .  Red
1 .  Green
2 .  Blue
3 .  Pink, with lilac stripes and technicolour swirls
Enter number of correct answer: 2
Correct!
What colour is Jango Fett's armour?
0 .  Pink, with lilac stripes and technicolour swirls
1 .  Green
2 .  Red
3 .  Blue
Enter number of correct answer: 3
Correct!
What colour is Jango Fett's armour?
0 .  Pink, with lilac stripes and technicolour swirls
1 .  Green
2 .  Red
3 .  Blue
Enter number of correct answer: 3
Correct!
What colour is Jango Fett's armour?
0 .  Red
1 .  Green
2 .  Blue
3 .  Pink, with lilac stripes and technicolour swirls
Enter number of correct answer: 2
Correct!

Do you see how the location of the correct answer changes each time the question is asked and that the code keeps track of where the right answer is?  Also, notice that it’s also possible for the correct answer to stay at location 0.  This is not a problem, as all that is important is that the correct answer is not always there.  Finally, notice that we’ve used the range() function here to automate the asking of the askQuestion() function.

Trivial Lists

…And the first question is for you, Karl Marx. The Hammers – The Hammers is the nickname of what English football team? ‘The Hammers? (shot of Karl Marx furrowing his brow- obviously he hasn’t a clue) No? Well bad luck there, Karl. So we’ll go onto you Che. Che Guevara – Coventry City last won the FA Cup in what year? (cut to Che looking equally dumbfounded) No? I’ll throw it open. Coventry City last won the FA Cup in what year? (they all look blank) No? Well, I’m not surprised you didn’t get that. It was in fact a trick question. Coventry City have never won the FA Cup…

Having gone through some arduous work on the previous tutorial, this one will be a little gentler.  The task we’ve set ourselves this time is to write another game,  a trivia game.

Exercise:

Make a list of the things that a trivia game will need to do.
Don’t go further until you’ve done your list.

Finished?

No, honestly, do your list.
Ok, well, these are the sorts of things that I think a trivia game will need to do:

  1. it will need to have a list of questions
  2. for each question it will need to have a correct answer and at least one other incorrect answer, but possibily more.
  3. it will need to put the question to the user and get the person’s response
  4. it will also need to check to see if the response is correct
  5. optionally it will need to keep score

Given that each question needs to have a number of answers, we’re going to store them in a list.  We will store the question as the first element (ie at index number 0) in the list, and the second element (ie at index number 1) we will use to store the correct answer.  The rest of the list will be a number (1-4) of incorrect answers.  Here’s the list for our first question.

>>> question1 = ["What colour is Jango Fett's armour?",
... "Blue",
... "Green",
... "Red",
... "Pink, with lilac stripes and technicolour swirls"]
>>> question1
["What colour is Jango Fett's armour?", 'Blue', 'Green', 'Red', 'Pink, with lilac stripes and technicolour swirls']

Note that we’ve used double quotes so that the apostrophe in Fett’s prints properly.  Also see how we’ve created the list, by putting a comma in between each entry and with new entries on each line (you could put them all on one line, this is just a little easier to read).

Now let’s write a function to ask the question and get an answer:

>>> def askQuestion(questionList):
...      print questionList[0]
...      for i in range(len(questionList)-1):
...          print i,'. ',questionList[i+1]
...      answer = raw_input('Enter number of correct answer: ')
...      if answer == '0':
...          print 'Correct!'
...      else:
...          print 'Wrong'
...

So, we define a list which has the question, the correct answer and some wrong answers in it.  The list defines the question that the askQuestion() function asks for us.  The askQuestion() function also checks whether or not the answer is the correct one.  Note that what is tested is ‘0’, not 0.  That is, a string not a number.  This is because the raw_input() function returns a string, not a number and strings are not numbers.

Also of note here is the len() function, which is used inside the range() function.  When you run the len() function on an object len(object) it returns the length of the object.   In the case of a list, it is how many elements there are in the list.  Also, we need to skip over the first entry in the list (which has the question in it – entry 0), so we take the range up to one less than the length of the list, then add one when we print.  This starts printing at the second entry and ends a the last entry.

Let’s pass our question (question1) as an argument to the function (askQuestion()) and see how it works:

>>> askQuestion(question1)
What colour is Jango Fett's armour?
0 .  Blue
1 .  Green
2 .  Red
3 .  Pink, with lilac stripes and technicolour swirls
Enter number of correct answer: 0
Correct!
>>> askQuestion(question1)
What colour is Jango Fett's armour?
0 .  Blue
1 .  Green
2 .  Red
3 .  Pink, with lilac stripes and technicolour swirls
Enter number of correct answer: 2
Wrong

We can make new questions using the same format.  The way we’ve set up the function we can have any number of wrong answers:

question2 = ["Who was Obi's Jedi Master?",
"Qui-Gon Jinn",
"Scooby Doo",
"Darth Maul",
"Beowulf",
"Amadeus Mozart",
"Yoda"]

>>> askQuestion(question2)
Who was Obi's Jedi Master?
0 .  Qui-Gon Jinn
1 .  Scooby Doo
2 .  Darth Maul
3 .  Beowulf
4 .  Amadeus Mozart
5 .  Yoda
Enter number of correct answer: 0
Correct!
>>> askQuestion(question2)
Who was Obi's Jedi Master?
0 .  Qui-Gon Jinn
1 .  Scooby Doo
2 .  Darth Maul
3 .  Beowulf
4 .  Amadeus Mozart
5 .  Yoda
Enter number of correct answer: 4
Wrong

But there’s a problem with this structure.  Can you see what it is?

Exercise:

Check to see we’ve done numbers 1-4 of what we said we’d do.  Also, think about what is wrong with the askQuestion() function?  Why would this trivia game not be an interesting game? (hint: look at the correct answers)

How might we change this so that it can be used to ask a number of questions? (we’d probably change the way we store the questions, and write some more code to ask the additional questions)

Consolidation: CryptoPyThy

General     Shi muska di scensand dravenka oblomov Engleska Solzhenitzhin.
SUBTITLE: ‘FORGIVE ME IF I CONTINUE IN ENGLISH IN ORDER TO SAVE TIME’

So far we have learned about:

outputting information via the print statement (implicitly, there wasn’t a specific tutorial on it)

getting information from the user (raw_input)

the difference between strings and numbers, and how to manipulate them a little

how to loop over a range of numbers

conditionals – the if statement

how to get a list of numbers from 0 up to a specified number (range) and the remainder operator %

conditional looping – the while statement

a bit about variables and how they can be used to store information

Some of the reasons why we’re learning Python and not some other language

The List type, and how they can be used to store similar sorts of information

Functions and how they can be used to transform the arguments that you pass to them

How to use other people’s functions through the import statement we imported a module someone else wrote called random and made a guessing game with it; and, finally

We learned a little more about the string type and some ways of cutting them up and putting them back together again

Enter CryptoPyThy

In retrospect, this is probably a bit too much to cover without a breather.  However, we’ve now got a good slice of the basic concepts down and are able to write a program which will  make use of them.  I’m calling it CryptoPyThy (‘crypt-oh-pih-thee‘) and we can use it to make secret messages that no one else can read.  What we will do is write some code which:

* asks for a message to encrypt (or decrypt);

* takes that message and passes it to an encryption function;

* the encryption function breaks it down to each character in the message;

* it passes each character to another function which encrypts the characters; and

* after it has been encrypted, prints the encrypted message on the screen (then translates it back to show).

Or, at least that’s what I wanted it to do, but there’s some glitch in my system which is replacing some of the code by ?? So, in this tutorial, you’re going to have to work out what should go wherever  ?? appears and replace the ??.

To start, I’m going to introduce you to a builtin function – on that you have automatically without having to import it (like range()).  It’s called chr().  The argument you pass to chr() needs to be a number (an integer in fact).  Then, chr() takes that number and returns a letter (which can then be printed). Examples:

>>> chr(65)
'A'
>>> chr(104)+chr(105)+chr(32)+chr(116)+chr(104)+chr(101)+chr(114)+chr(101)+chr(33)
'hi there!'

If you want to see a list of characters try this:

>>> for i in range(32,127):
...     print chr(i),

While I’m here, I want to note that: we’ve got two numbers (ie arguments) in the range function (the first number is now where it starts from), but earlier we only saw one.  Also, we’ve put a comma at the end of the print statement.  This tells Python to keep printing from the same place next time it prints (normally it will start a new line).

There is a reverse function to chr(), and which takes a character and returns a number is called ord().

>>> ord('A')
65

So, chr(65) gave ‘A’ and ord(‘A’) gave 65.  You can test it by doing these:

>>> chr(ord('A'))
'A'  <- ord converted 'A' to 65,
        then chr converted 65 back to A
>>> ord(chr(65))
65   <- as above, but reversed.

We’re going to define our encryption function as follows:

>>> def encryptChar(character=None):
...    if character is None:
...      return ''
...    something = ??
...    spam = ord(character) + something
...    if spam > 127:
...      spam = spam - (128-32)
...    return chr(spam)

Put a number in where the question marks are.  It needs to be that number which, when doubled and then added to 32, equals 128.

Exercise: work out what the number should be and replace ‘??’ by that number in the something = ?? line in the function’s definition.   Hint: you can use Python to do the calculations for you and work backwards from 128 (actually, that’s two hints).

Explanation of the code:

The function encryptChar() expects to receive a single character as an argument.  If no argument is passed in, the character defaults to None (not ‘None’ note quotes – Python actually has a value called None).  If the character is None, then return an empty string.  Otherwise, calculate the number representing the character and add something to it, so the number we now have represents another character.  Unfortunately, we’re limited to characters represented by the numbers 32 through 127 (this was decided many decades ago, don’t question it).  So if, after we’ve added something to it, we have a number bigger than 127, we need to get the number back in that range.   And, in particular, we want 128 to go to 32 (so what’s the number, which when added to 128 gives 32? [1]

Next we need a function which takes a message and splits it into characters.  We could do this by using the [:] operator we met earlier to extract each character one by one – eg message[0:1] etc.  However, we’d need to know how to work out the length of the message, so we’d know when to stop (answer = the len() function).  Rather, we’re going to rely on a neat thing about strings – they have their own implicit iterator. Let me explain by way of an example:

>>> for i in 'ABCDE':
...     print i
...
A
B
C
D
E

So, when Python encounters a statement like for i in someString: in the for loop it assigns to the variable i each of the characters in the string, one by one.  We will use this to split our message up:

>>> def encryptMsg(message= None):
...   if message is None:
...     return ''
...   encrypted = ''
...   for character in message:
...     encrypted += encryptChar(??)
...   return encrypted

As with the encryptChar() function, this function expects a message as its first argument, and defaults the message to None if none is provided.  Then it checks to see if the message is None, and if so, it returns an empty string -> .  As above you need to work out what goes in the place where the question marks are ‘??’.   The encryptChar function expects to receive a single character,  so the ?? need to be replaced by a variable which contains a single character.

Finally we need a way of inputting a message – make sure you replace ?? by  a function call that we’ve seen before for getting user input.

>>> def cryptoPyThy():
...   while True:
...     message = ??('Type your message for encryption: ')
...     encrypted = encryptMsg(message)
...     print 'Encrypted message = \n',encrypted
...     print 'Decrypted message = \n',encryptMsg(encrypted)
...     print
...     
...     if message == 'q':
...       break
...
>>> cryptoPyThy()
Type your message for encryption: This is a secret message
Encrypted message =
$89CP9CP1PC53B5DP=5CC175
Decrypted message =
This is a secret message

Note that this is a special encryption function – if you encrypt the encrypted message, then you get the original message (called the plain text)  back.  You can see that from the printout above.

Extra Points:

(hard): This code replaces one character by another in a consistent way.  Someone who was determined might be able to decrypt the messages if they see a lot of them – or if they see the plain text of the message. In fact, this is the correspondence:

>>> spam = ''
>>> for i in range(32,128):
...     spam +=chr(i)
...
>>>
>>> print spam+'\n'+encryptMsg(spam)
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO

(Easy):  explain what this bit of code did.

(Hard): Change the functions so that the amount of the offset (that is, the variable something) can be passed as an argument to the encryptChar() function.   That way you could encrypt each message slightly differently – but you’d need to tell the recipient the offset so they could decrypt the message (how could you use encryptChar to decrypt one of these messages? – hint: think of negative numbers).

Notes:

[1] Answer ->

128+x = 32

=> 128+x-128 = 32-128

=>  128-128 on the left hand side is zero, so

x = 32-128,

but if we want to subtract x, we add its negative

=> -x = -(32-128)

=> -x = 128-32

String

String

I have this large quantity of string, a hundred and twenty-two thousand miles of it to be exact,…

We have already met strings indirectly throughout some of the other tutorials.  If you’ll recall, strings are the things you get when you put inverted commas around stuff – like this: ‘stuff’.   As far as Python is concerned, the inverted commas transform what might be other sorts of data into a string.  There are actually two three four Amongst our ways of making a string are such diverse elements as: using inverted commas (‘), using quotation marks (“) , using triple inverted commas (”’) [update:] and triple quotes (“””).  Which sort you use depends largely on your preference.  Sometimes, you need to use a certain manner of creating the string because the string itself contains (eg) an inverted comma.

>>> ‘a string’
‘a string’
>>> ‘This isn’t really a string because the inverted comma in the middle cuts the string off early.’
File “<stdin>”, line 1
‘This isn’t really a string because the inverted comma in the middle cuts the string off early.’
^
SyntaxError: invalid syntax
>>> “However, you can use quotation marks when there’s a need to include an inverted comma in a string”
“However, you can use quotation marks when there’s a need to include an inverted comma in a string”
>>> ‘Conversely, if you need to use “quotation marks” in a string, you can make the string using inverted commas’
‘Conversely, if you need to use “quotation marks” in a string, you can make the string using inverted commas’
>>> ”’Finally, you can pretty much always use triple inverted commas.  When you do, you don’t have to worry about whether there are “quotation marks” in the string or inverted commas.”’
‘Finally, you can pretty much always use triple inverted commas.  When you do, you don\’t have to worry about whether there are “quotation marks” in the string or inverted commas.’

Can you see, in the last string, Python has represented the ‘ by using \’?  This is called ‘escaping’ a character.  Some escaped characters have specific meanings.  The ones which will see most often are: \t which means tab, and \n which means new line:

>>> print '1\t2\n3'
1       2
3

When Python printed the string it didn’t actually print ‘\t’ it actually interpreted it as a control character and put a tab space between the 1  and the 2 (and a new line between the 2 and the 3).

Cut String

Mr. Simpson:     Ah, but there’s a snag, you see. Due to bad planning, the hundred and twenty-two thousand miles is in three inch lengths. So it’s not very useful.
Wapcaplet:     Well, that’s our selling point! “SIMPSON’S INDIVIDUAL STRINGETTES!”

Sometimes  you don’t want the whole of the string.  Sometimes you just want a bit of it.   Python comes with a number of ways to chop up strings (and to put them back together again).  The chop function (actually a method, but that’s for another time) is called .split() and the function for putting them together is called .join() (if it flows off the side where you can’t see it you can still copy and paste it – sorry):

>>> simpson = '''the hundred and twenty-two thousand miles is in three inch lengths'''
>>> print simpson
the hundred and twenty-two thousand miles is in three inch lengths
>>> simpsonSplit = simpson.split(' ')
>>> print simpsonSplit
['the', 'hundred', 'and', 'twenty-two', 'thousand', 'miles', 'is', 'in', 'three', 'inch', 'lengths']
>>> print ' '.join(simpsonSplit)
the hundred and twenty-two thousand miles is in three inch lengths

Can you see that .split(‘ ‘) seems to have ‘split’ the string (it’s actually still there, the ‘pieces’ are in a separate list object) .  It split it wherever there was a blank space (‘ ‘).  We could have easily split it on a different (sub-)string, like the letter ‘e’:

>>> print simpson.split('e')
['th', ' hundr', 'd and tw', 'nty-two thousand mil', 's is in thr', '', ' inch l', 'ngths']

You should also note that: there is a dot . at the front of .split(); its usage is someString.split(‘someOtherString’); and after .split() split the string, we had a list of strings. Unlike poor Mr Simpson, if our strings are chopped up into little pieces, we can .join() them back together again.  The structure of .join() is the reverse of .split().  At the front of .join() you put the thing you want to join the strings with.  In the brackets you put a list that you want to join up (it must be a list). As with .split() you can put any string as the joining string.  See what you get from:

>>> print '\n'.join(simpson.split(' '))

You can .split() and .join() things in chains if you really want to, to make some 133t speak:

>>> '1'.join(('4'.join('3'.join(simpson.split('e')).split('a')).split('l')))

‘th3 hundr3d 4nd tw3nty-two thous4nd mi13s is in thr33 inch 13ngths’

Due to Bad Planning

You can also slice strings up, but without actually cutting them.  You do this by using the [:] operator.  It takes up to two number (actually int)  arguments. Instead of explaining I will give you some examples:  (the comments after <- I have typed in after don’t expect to see them in your output)

>>> aString = '0123456789ABCDEFGHIJ'
>>> aString[0]
'0'  <- get the first character (number 0) in the string
>>> aString[1]
'1' <- get the second character (number 1
       - yes, 1 just believe on this) in the string
>>> aString[10]
'A' <- get 11th character
>>> aString[1:1]
''  <- get the characters from the second character up to,
       but not including the second character - ie empty
>>> aString[1:2]
'1' <- starting on the second up to
       but not including the third (ie number 2)
>>> aString[1:3]
'12' <- starting on the second utbni the fourth (number 3)
>>> aString[1:10]
'123456789' <- starting on the second utbni the 11th
>>> aString[:10]
'0123456789' <- starting from the start of the string
                utbni the 11th
             <- note there are 10 numbers in the string
             <- note also nothing in front of the colon
>>> aString[10:]
'ABCDEFGHIJ' <- starting from the 11th to the end of the string.
             <- nothing after the colon
>>> aString[9:]
'9ABCDEFGHIJ'<- starting from the 10th to the end of the string
>>> aString[-1]
'J' <- last character putting a - before the number says start
       from the end of the string and work back
>>> aString[-4:]
'GHIJ' <- last 4 characters
>>> aString[:-10]
'0123456789' <- starting from the start up to but not including
                the 10th character from the end
>>> aString[:-11]
'012345678' <- utbni the 11th character from the end
>>> aString
'0123456789ABCDEFGHIJ'  <- the string has not changed by doing
                           any of this
>>> start=5
>>> end=12
>>> aString[start:end]
'56789AB'   <- you can even use variables around the colon

HomeWork:

Change this for loop so that it separately prints each character in aString above:

>>> for i in range(20):
...     print i,': '+ aString[10]

Change the following to store the name of your school in the variable a.  Then use aInBits = a.split() to break it on the ‘o’s, and .join(aInBits) to join it up with zeroes where the ‘o’s where. You need to work out what goes in the brackets  of a.split() and what goes in front of .join(aInBits) hint: it’s a string and it has zero in it:

a='Baloney Public School'

Extra (or if there were no os in your school’s name):

store what you’ve done and replace the ‘e’s with ‘3’s, the ‘a’s with ‘4’s, the ‘S’s with ‘$’s and the ‘b’s with ‘6’s (you can recycle the variable aInBits)


Random Imports

In 1970, the British Empire lay in ruins, and foreign nationals frequented the streets – many of them Hungarians…

Hungarian: Ya! See-gar-ets! Ya! Uh…My hovercraft is full of eels.

In our last tutorial, we met the concept of functions.  It turns out that functions (and another concept called classes, which we haven’t got to yet) are the workhorse of Python.  Almost everything you will end up doing with Python will involve the use of functions.  The good news is that most of what you do will rely on functions written by other people.  Python includes a mechanism for sharing functions with other people, but also with yourself – so if you write a function for one program today, you can use it with a different program tomorrow (the multiply() function we wrote earlier might be useful in many different sorts of programs for example).  That mechanism is called the import statement.  If you’re unfamiliar with this English word ‘import’ see this definition.

The import statement imports programs which have been saved [into another file – the file is called a module,  but we haven’t done saving files yet] into the current program.  For the time being we will only be importing modules which other people have written.  However, later, when we get to saving programs we may also be importing our own.

You have already used the import statement in the WhyPy tute:

>>> import this
[stuff omitted]

It told us about the Zen of Python.  What is happening here is that there is a file called ‘this.py’ (and is hidden away in /usr/lib/python2.5/this.py on the computer I am typing this on, but it may be somewhere else on yours).  When you import this, Python loads and runs the contents of that file as if the contents of the file were executed at the place where the import statement is – with some caveats (see below).  The this.py file contains two for loops and a print statement at the end of it (if you are brave and able, find a copy of the file on your system and read it).  When the module is imported Python actually acts on these statements as if you’d typed them in, which is why you get the printout on import.

When there is another function defined in the imported program, it is given a name as a part of the module imported.  So if the function multiply() is defined in a file called myModule.py then import myModule (note no ‘.py’) would create a function in your program which is called myModule.multiply().  To the  computer this means “look for a function called multiply() in the file myModule.py”. As I mentioned earlier we’re going to look at importing other people’s stuff first.  Importing our own stuff will need to wait.

Python comes with a heap of functions built in.  That is, they are available when you run the Python interpreter, without having to explicitly import anything further.  The range() and int() functions are examples – they are available whenever you run Python.  However, a Python installation also comes with other modules which are available for loading, but which are not loaded automatically (loading everything every time would use up time and memory on the computer, and if the module’s functions aren’t being used, that would be wasted effort).  The random module is an example.

>>> random()
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
NameError: name 'random' is not defined
>>> import random
>>> random()
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
TypeError: 'module' object is not callable

Here I have tried to use a function called random() – but Python doesn’t know what I’m talking about.  However, after I import the module called random it gives me a different response (it says that it knows about something called ‘random‘, but it isn’t callable – that is, it can’t be treated as a function).  The name of the module here is ‘random‘.  It also has a function within it called ‘random()‘.  So the way we call it is by putting the module name and the function name together, separated by a dot:

>>> random.random()
0.17113600109807192
>>> random.random()
0.82195215747538508

What random.random() does is to give you a random (technically ‘pseudo-random’) number between zero and one, including zero, but not including one.  Randomness can be very important in programs which rely on variability.  You may not want randomness when you multiply numbers together, but you might want it in a game where aliens appear at random locations on the screen.

Guessing Game

Here’s a quick guessing game, using a function definition and the random() function from the random module (ie random.random()):

>>> import random
>>> def guessGame():
...   answer = int(random.random()*10)+1
...   while True:
...      spam = raw_input('What is your guess (type a number): ')
...      guess = int(spam)
...      if guess > answer:
...         print 'No, the answer is less than ',guess
...      if guess < answer:
...         print 'No, the answer is greater than ',guess
...      if guess == answer:
...         print 'Yes, you guessed correctly! The answer is ',answer
...         return
...
>>> guessGame( )
What is your guess (type a number): 3
No, the answer is less than  3
What is your guess (type a number): 2
Yes, you guessed correctly! The answer is  2
>>> guessGame( )
What is your guess (type a number): 4
No, the answer is greater than  4
What is your guess (type a number): 6
Yes, you guessed correctly! The answer is  6

Each time you run it, the answer should be different (well, one in ten times it will be the same as the last answer).

Homework:

[This will be a bit tedious.  When we know how to save files it will become a little easier]

Change it to a guessing game for numbers from 1 to 100 (or 1000 etc) by changing the 10 to a different number.

Extra points: make the number range dependent upon a value passed into the function – for example, guessGame(17) would choose a number from 1 to 17. In order to do this you should look at the function tutorial – the function’s definition needs to be changed so that it has an argument in the brackets.  Give the argument a name, and then use the name of the argument where 10 is above.

Explanation:

The function first initialises an answer by randomly choosing a number between zero and one, multiplying it by 10 (giving a number between 0 * 10 and 1 * 10 – but not including 1*10).  It uses the int() function we met earlier to convert it from a decimal (like 3.4) to an integer (like 3).  This gives a number between 0 and 9 (because random() won’t return the number 1),  so we add one to make the answer between 1 and 10.    Then the program falls into a continuous loop which keeps asking you for a guess.  If the guess is wrong, it tells you whether you need to guess higher or lower.  If you’re right it congratulates you then exits the function through the return statement.

Do you notice that you run the function by putting the name of the function with no arguments-> guessGame().  Did you also notice that you can keep on running the function without having to retype it, just by typing guessGame()?  Functions save you a lot of effort in this way.

To show you that the import really has an effect, we can delete the [reference to the] imported module (using del, which I haven’t told you about yet) and show that guessGame() no longer works without it, then re-import it:

>>> del random
>>> guessGame( )
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "<stdin>", line 2, in guessGame
NameError: global name 'random' is not defined
>>> import random
>>> guessGame( )
What is your guess (type a number):

A final Note

On a final note, you might, reasonably, ask how I knew that there was a module called random which was available to import.  Unfortunately, there isn’t an easy way to know, other than reading through the Python docs, seeing it mentioned in a book or Googling what you want.   We’ll cover some useful imports in some other tutes.

Functions (or The Right Place for an Argument)

I have a secret to tell you.   I have been cutting some corners in some of the earlier tutorials.  Wherever you saw these -> () I was sneaking a function in under your nose, without actually telling you about them.

Here is a sample function:

>>> def justContradiction(argument):
...     return 'Yes I did'
...
>>> justContradiction("You did not")
'Yes I did'
>>> justContradiction("didn't")
'Yes I did'
>>>



justContradiction Function takes one argument

What is happening here is:

  • def is a Python statement.  It says to the program that you are def-ining a function;
  • the function’s name (in this case) is justContradiction – not entirely inapt, as it indulges in the automatic gainsay of whatever argument is put to it (try justContradiction(5));
  • the function named justContradiction expects to receive one input (called an argument);
  • the function is called by putting the name of the function and putting a single argument in brackets immediately after the name of the function (other functions may take a different number of arguments);
  • when the function is invoked by a call (the lines which start with justContradiction…) Python actually goes to the function and works through it.  When (and if) it encounters a return statement, Python leaves the function and comes back to where it left off and passes the value returned back as well.  As we are in interactive mode, the Python interpreter prints it, much as it would printed the value of toy0 in the Lists example.  If the function’s code block ends without a return statement, the program still continues where it left off, but no value is returned.

This function is a little boring because it is unresponsive to the argument put to it (not unlike Mr Vibrating).  Functions get more interesting though when they do something with the arguments they are called with.  (For your reference, the functions I sneaked in earlier were range(), raw_input() and int()).

Here is a function which transforms  squares into circles:

>>> def f(shape):
...    if shape =='square':
...         return 'circle'
...    return shape
...
>>> f('box')
'box'
>>> f('circle')
'circle'
>>> f('square')
'circle'

So, as above, we have defined a function (called f).  This function receives an argument and, when it does ,it calls that argument shape, although any valid variable name would do.   It checks to see if what was passed to it was the string ‘square’.  If so, it returns ‘circle’.  If not, it returns what was passed (return shape).  Functions are not fussy about the arguments passed to them, although they will get stroppy if you pass in the wrong number of arguments (ie you put more arguments in the brackets than are there in the definition of the function).

>>> f(9)
9
>>> f()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: f() takes exactly 1 argument (0 given)
>>> f('square','circle')
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: f() takes exactly 1 argument (2 given)

Functions can be defined to take any number of arguments, but they must be separated by commas.  Also, you can make some arguments optional by supplying a default value for them:

>>> def multiply(a,b=1):
...    return a*b
...
>>> multiply(2)
2
>>> multiply(2,3)
6
>>> multiply(2,3,4)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: multiply() takes at most 2 arguments (3 given)

Here, we’ve defined a function called multiply() (I am including brackets here to emphasize that it is a function).  It takes 1 or 2 arguments. If no second argument is supplied it is assumed to be 1 (b=1) – you probably wouldn’t want  multiply to do this in the wild though.  In the example multiply(2,3), when multiply() is called the first argument in the calling part of the program (2) is assigned to the first named variable in the definition (a) and 3 is assigned to b (later, when b is omitted, the value of a is returned).  The two numbers are multiplied together (a*b) and immediately returned to the calling part of the program.  Moreover, a function call can play the role of any other value:

>>> a = multiply(2,3)
>>> a
6
>>>

Here it is an assignment  (2*3 is assigned to the variable a – this is a different a from the one in the function, this aspect of functions won’t be covered in this tute, but if you’re curious search for “python local variables” ).  But you really can treat the function call as any other object:

>>> multiply(multiply(2,3),4)
24

Here multiply is called twice.  First, the computer works out what multiply(2,3) is (hint: it’s 6).  Then it works out multiply(6,4).  Just so you don’t get complacent:

>>> multiply('Yes I did ',4)
'Yes I did Yes I did Yes I did Yes I did '
>>>

Functions have many functions (errr… so to speak).  Some ways functions can be used include:

  • to avoid having to retype the same section of code over and over;
  • to create logical units of code;
  • to give a meaningful name to certain parts of code;
  • ease of updating.  If you have cut and paste code at different parts of your program and want to change how it operates, you need to hunt down everywhere in your program where you put the code.  However, if the code is in a function any changes you make are automatically updated everywhere because all calls go through the function’s code.

Homework:

Change this siblingAge function so that if you type in your age as an argument, it returns the age of one of your siblings (or, if you have none, of an imaginary sibling who happens to be 2 years younger than you):

>>> def siblingAge(myAge):
...     return myAge-0
...
>>> siblingAge(8)
8

Use it to find how old the sibling will be when you are 15, 30 and 47.

Each time you change it you need to retype the def statement line, then the return line.   The arrow keys should allow recall of lines you have typed earlier.

Lists

You might recall earlier we talked about two ‘types’ of data in a program – ‘strings’ and ‘numbers’.   We learnt that you can tell something is a string because it has quotation marks around it.  In this tutorial we are going to meet a new type called lists.   You can’t program a computer game without knowing about lists.  You can tell something is a list because it has square brackets around it -> [].  The truth is I’ve snuck a list in before, so this is not a first meeting.  Do you remember?

In the for/range tutorial we used range(5).   It gave this output:

>>> range(5)
[0, 1, 2, 3, 4]

The object [0, 1, 2, 3, 4] is a list.

To adopt the analogy we used for variables, lists are like a big bucket holding a heap of smaller buckets, although the smaller buckets don’t have names, they just have numbers.  Lists are a very powerful way of allowing you to handle a heap of things in a consistent and general way without having to deal with each of them specifically.

For example, imagine you have two toys.   Let’s say toy0 is an action figure, and toy1 is a scooter.  When you want to store these you would do an assignment like this:

>>> toy0='action figure'
>>> toy1='scooter'
>>>

This stores the toys, but it does so in a way which is difficult to deal with in a program.  We will see why in a moment.   After you have made these assignments you do some other things in the program and you get to a point where you want to do different things depending on what toys you have.  You’d have to code something like this:

>>> toy0='action figure'
>>> toy1='scooter'

#do a heap of other stuff, which we’re not detailing here!

>>> if toy0 == 'scooter' or toy1 == 'scooter':
...    print 'We can scoot!'
...
We can scoot!

So after doing stuff in the program we come to a decision point with the if statement.   In order to code the  decision point we need to know ahead of time what the names of the individual variables holding the toys are.  Then you can get the computer to check each of the variables to see if either of them is a scooter.  If it is, then it tells us we can scoot.

Your scooter is a little old and you were getting a little bit big for it, so you swap it with your friend at school for a pair of binoculars, so toy1 needs to change:

>>> toy1 = 'pair of binoculars'
>>> print toy0+', '+ toy1
action figure, pair of binoculars

Then, long lost Auntie Annona sends you a late birthday present – a new scooter.  You can’t put in in the toy1 bucket because you’re already storing your binoculars there.  Instead you store it in a new variable toy2 (since there was already something in toy0 and toy1 that you wanted to keep), not where it originally was.   So

>>> toy2 = 'scooter'
>>> print 'my toys are: '+toy0+', '+toy1+', '+toy2+'.'
my toys are: action figure, pair of binoculars, scooter.
>>>

However, since you’ve stored it in a new place your program won’t work properly anymore:

>>> if toy0 == 'scooter' or toy1 == 'scooter':
...    print 'We can scoot!'
...
>>>

Despite having a scooter (and therefore you are scoot-enabled), the program fails to tell you so because it is looking in the wrong place to find it.  The problem here is you won’t know when you write the program how your toys will change over time, so you can’t write the program to anticipate the different changes of toys which may occur – unless, that is, you do the rest of this tute and learn about lists.

Toys as  a List

Let’s implement the same program as a list:

>>> mytoys = ['action figure','scooter']
>>> mytoys[0]
'action figure'
>>> mytoys[1]
'scooter'
>>>

Instead of storing the toys in different variables, a list stores them in different places within the same (list) object.  The first place in a list is (somewhat misleadingly) called number 0.  The second place is number 1, the third number 2 and so on.   Don’t ask why, just accept it.  It’s like some people saying the week starts on a Sunday, and others saying it starts on a Monday.  Here, mytoys[0] (note the square brackets) says ‘show me slot zero of the list called mytoys‘.

We can implement the same code as above and get the same sort of outcome:

>>> if mytoys[0] == 'scooter' or mytoys[1] == 'scooter':
...    print 'We can scoot!'
...
We can scoot!
>>>

Notice how toy0 and toy1 (separate variables) are now mytoys[0] and mytoys[1] (list items). When we swap the scooter for binoculars we can do it like this:

>>> mytoys[1]='pair of binoculars'
>>> mytoys[1]
'pair of binoculars'
>>>

Now, when we get the new scooter from long lost Auntie Annona, we can use one of the clever things about lists: we can just add something to the end of it – we don’t need to add another variable or name it.  In particular, above, in order to call our new variable toy2, we needed to know that the previous one was called toy1.  This is not necessary for lists:

>>> mytoys = mytoys + [‘scooter’]
>>> print ‘my toys are: ‘+mytoys[0]+’, ‘+mytoys[1]+’, ‘+mytoys[2]+’.’
my toys are: action figure, pair of binoculars, scooter.
>>>

Note: we are adding [‘scooter’] (that is, a list [] containing a single item ‘scooter’) not ‘scooter’ (a string) – adding ‘scooter’ will give a type exception (try it and see).

Unfortunately, our conditional still fails with the list:

>>> if mytoys[0] == 'scooter' or mytoys[1] == 'scooter':
...    print 'We can scoot!'
...
>>>

However, if fails because we haven’t been using any list-Fu (ie any special list skills – probably because we haven’t learnt any yet) not because there’s something wrong with lists.  Let’s revisit the earlier code.

One of the really powerful things about lists is you can leave it up to the computer to remember how many things are stored in the list.   So, while I’ve explicitly referenced mytoys[0] and mytoys[1] here,  I could just have easily said simply mytoys and the computer will work out what is in the list:

>>> mytoys = ['action figure','scooter']

>>> mytoys
['action figure', 'scooter']

This turns out to be very important.   Pay attention to the square brackets here, it says this is a list.  Each of the items in the list is separated by a comma ( , <- ie this thing).

Next, lists have a concept of containment.  You can ask the computer to just check whether something is in the list or not, without caring where in the list it is.  So we can rewrite the code like this:

>>> if 'scooter' in mytoys:
...    print 'We can scoot!'
...
We can scoot!
>>>

Wow! Did you see that?  This way of programming means it doesn’t matter how many items are in the list, or where in the list your toy is, this piece of code will always work to find whether it is in the list.

When we swap the scooter for the binoculars we could use our special list skills to do it without knowing where in the list the scooter was, but we won’t for now (try >>> help(list) and the remove method if you’re interested).

>>> mytoys[1]='pair of binoculars'
>>> mytoys[1]
'pair of binoculars'
>>>

Now add the new scooter again:

>>> mytoys = mytoys + ['scooter']
>>> mytoys
['action figure', 'pair of binoculars', 'scooter']
>>>

See how we can print the whole list out without knowing how many entries it has.  Up above we needed to know ahead of time that the variables were called toy0, toy1 and toy2 to print them all out.

Now test whether we can scoot:

>>> if 'scooter' in mytoys:
...    print 'We can scoot!'
...
We can scoot!
>>>

W00t! We can scoot!  This would still work if you received a heap of presents and then got your scooter, so that scooter was at the end of (or hidden in the middle of) a very long list.

Lists (and their use as iterators – something we will look at later) are super important in practically all graphical games. If you can imagine writing something like a space invaders game, you would keep all of the aliens in a list.  As they are shot, you would remove them from the list and as more arrived, you would add them.

Homework

Make up some of your own lists and test whether particular items are in them or not.  Build a list by putting square brackets around a list of entries separated by commas.  If the entries are not numbers, put inverted commas around them to make them strings.  When testing whether a string is in or out, remember to put it in quotes:

>>> numberList = [1,2,3,5,45,345,6]
>>> 345 in numberList
True
>>> 7 in numberList

False
>>> stringList = ['a','this is a string','ho hum','baloney']
>>> 'baloney' in stringList
True
>>> baloney in stringList
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
NameError: name 'baloney' is not defined
>>> 'palaver' in stringList
False

Also try printing your list and printing specific entries:

>>> numberList
[1, 2, 3, 5, 45, 345, 6]
>>> numberList[0]
1
>>> numberList[4]
45
>>> stringList[2]
'ho hum'

WhyPy?

The Zen of Python

>>> import this

(Don’t read it, do it)

What is Python?

Python is a computer programming language. It is named after the comedy act Monty Python (not after the snake of the same name).  It was created by a mathematician called Guido van Rossum.   Python is called a “scripting language” because what is typed by the programmer can be run directly by the computer.  Unlike some other programming languages, Python programs do not need to be “compiled” before they work (actually they do, but the computer does it without you noticing).

Why Python

Ultimately, the reason these tutes are on Python is because Python rocks!!, and it rocks in so many ways:

For a start, the Python language is extremely, sublimely beautiful.  When I say beautiful, I don’t mean in a La Gioconda sort of sense – although it’s code indentation convention does make it visually attractive.  Rather, I mean it in a Galois Theory sort of sense.  That is, it has a beautiful simplicity and consistency from which comes a deep power (don’t expect to understand the Galois Theory reference until you’ve made it to college – I think it is no co-incidence that Guido van Rossum was a mathematician).  As you learn more about how Python does what it does you will constantly think how clever its creators were.

It’s a scripting language – in practical terms this means it is easier for children to understand and implement.

Python uses visual cues (ie indentation) to mark different parts of code.  Other languages use markers like () and {} to mark different parts of code.  This can lead to much difficulty when trying to match any given closing } back to its initiating {.  Particularly where there are multiple levels of code within one another.  In other words, Python programs (including your own) are typically easier to read and understand.

Python is dynamically typed.  In practical terms, this means you don’t need to understand very much about how computers store and manipulate data in order to program in Python.  Other languages make you understand the difference between (say) a string and a number (or between different sorts of numbers) in order to simply define a variable before you even try to store anything in one.   You’d be asleep before we got anywhere in these tutes.

Python is cross platform.  Programs you write in Python can be run on computers running different sorts of hardware or different sorts of operating system without any changes.  Other languages typically involve non-trivial rework.  The language itself is also available on many different architectures and operating systems.

Python is free software.  This means not only can you download a copy for free, but you can look at the source code to see what is going on.   Some people believe that free software is also more ethical than non-free software.

Python has extensions like PyGame.  These mean we can do some fun stuff in the not-too-distant future.

Python has an established community and online documentation if you need it.  So I’m not your only source of knowledge.

There are many other reasons Why Python.   These are just a few.

Some Foundations: Variables and stuff

In some of the earlier tutorials I’ve been using variables without actually explaining what they are.  Strict Python-Zen: within Python there are no variables everything is an object*.   On the assumption that that is too abstract for you, I will explain it a different way, through the use of containers (like trays and buckets) which, if I’m lucky, will preserve at least a little bit of the Python-Zen.

Variables allow you to be flexible.  They are a way of storing something in the computer, much like having a bucket for putting your Lego in.  If you need to go do some chores and just drop your toys, you might not be able to find them again later – or if your parents clean up the toys might get thrown out in the course of garbage collection.   If, instead, you get a plastic bucket out and put them in there, you can rest safe in the knowledge that they’ll be there later after you finish your chores.  Variables are a bit similar.  They’re somewhere that you can put data that the program is using and label it for future reference.  Just like a bucket with the word “My Toys” on the side, you know that, assuming you put it in there earlier, your toys  will be in there next time you look.

Indeed, the name you put on the outside of the bucket doesn’t really affect what you put in it.  You could have the words “toy cars” on a bucket and yet put all your trading cards in there instead.   The point is that the bucket is a safe place to keep your toys.  Variables are a safe place to keep the program’s data.

When you do something like this:

a = 1

It is like putting 1 into a bucket marked a.   You can call variables almost anything – their name can’t contain spaces and can’t start with a number.  There are different conventions used to name things which really should have spaces in them, typically by capitalising a letter:

>>> 2a = 1
 File "<stdin>", line 1
 2a = 1
 ^
SyntaxError: invalid syntax
>>> a b = 1
 File "<stdin>", line 1
 a b = 1
 ^
SyntaxError: invalid syntax
>>> myToys = 1
>>> myToys
1
>>> mytoys = 2
>>> mytoys
2
>>> myToys
1
>>>

There are a couple of things happening here that I want you to notice.  First up are some examples of names which don’t work.  These are 2a (starts with a number) and a b (has a space).   Second I’ve shown another variable name which has a capital in the middle of it (myToys).  Do you see how the middle capital helps you to betterUnderstandWhereTheWordsAreSupposedToBeSplit?  – that is hard to read, but not as hard as betterunderstandwherethewordsaresupposedtobesplit.  I’ve also shown you another variable name with no capitals in it (mytoys).  Can you see that myToys and mytoys are different buckets?  If you put something in the myToys bucket, you won’t find it in the mytoys bucket and vice versa.  Variable names are said to be “case senstive”, because whether you use upper case or lower case matters.

Also, you can’t give variables names which are the same as words in the Python language:

>>> for = 1
 File "<stdin>", line 1
 for = 1
 ^
SyntaxError: invalid syntax
>>> if = 1
 File "<stdin>", line 1
 if = 1
 ^
SyntaxError: invalid syntax
>>> while = 1
 File "<stdin>", line 1
 while = 1
 ^
SyntaxError: invalid syntax
>>>

With buckets you can put different toys in them at different times.   A bucket marked “Cars” might have cars in, or equally it might have some other collection of stuff in it.  The bucket itself doesn’t have a character.  It just holds things.  So, you can put different ‘types’ into a bucket without causing any ruckus:

>>> a = 1
>>> a
1
>>> a = 'hi there'
>>> a
'hi there'
>>>

In this example, we first put a number (1) into a.  Then we put a string (‘hi there’) into it.  In some programming languages this causes a problem, but it doesn’t bother Python.

Finally, I should demonstrate to you that the variable stores the data:

>>> a = 'This is some data.  Will it still be here at the end of the program?'
>>> for i in range(5):
...    print 'doing stuff, time number ',i
...
doing stuff, time number  0
doing stuff, time number  1
doing stuff, time number  2
doing stuff, time number  3
doing stuff, time number  4
>>> a
'This is some data.  Will it still be here at the end of the program?'

The program first put data into a, then did some stuff, then saw that the data was still there, just as we’d expected.  Buckets and other containers play a vital role in keeping your room, the living room, the dining room, the kitchen, the bathroom, the library, the sun room, the garage, the front yard, the back yard, the attic, the space in our bedroom that you sometimes seem to think is yours, that area beside the stairs, and the family room clean of your toys (which seem to have a life of their own in the way they spread themselves around the floor).   So too, variables are extremely useful and play a fundamental role in all Python programs other than the most most basic.

* In Python, variables are actually references to objects.  They are an index telling the computer where in storage to find an object.   This is why you can change the ‘type’ of the thing being ‘held’ by a variable because it’s not really holding anything, it’s just pointing somewhere, and you can change what it’s pointing at.

While

The structure of a while statement is:

while <condition>:
    <do stuff>

The program answers the question: is the condition true or false? If it is true, it does stuff in the <do stuff> section.  When it is finished it goes back up to the top and repeats the process again and again until the condition is false:

>>> a = 10
>>> while a > 0:
...    a = a-1
...    print a
...
9
8
7
6
5
4
3
2
1
0

The ordering here is a little odd because I forgot to print a in the first line.  What is printed is the value of a after 1 has been subtracted from it. Note now that the value stored in  a has been changed while the program did that stuff.  It is now 0 (zero, not that letter in the alphabet after n).

>>> a
0

Anything that can be put in the <condition> part of an if statement can also go in while‘s <condition> statement.

>>> while True:
...    b = raw_input('Type stuff here: ')
...    if b == 'quit':
...         break
...    print 'You typed -> '+b
...
Type stuff here: hi
You typed -> hi
Type stuff here: what's this do?
You typed -> what's this do?
Type stuff here: hello? Is someone there?
You typed -> hello? Is someone there?
Type stuff here: quit
>>>

In this example, we’ve used a weird condition- True.  The computer, when asked the question, “Is True true or is it false?” will have to answer “True is true!”.  It will do this every time the condition is encountered.  Therefore this is a loop which would go on for ever.  Or, it would go on forever if we hadn’t used another instruction break.  What break does, is it ‘breaks’ out of the loop created by while and continues its execution at the next line following the <do stuff> block.  You should be able to see this because it didn’t print out “You typed -> quit” when I typed quit.  That code was skipped.  Break will also work to break out of a loop for the for command.

>>> for a in range(20):
...          print a
...          if a == 10:
...               break
...
0
1
2
3
4
5
6
7
8
9
10

Do you see it broke out of the loop when a was 10?

You can give set up an initial condition before starting the while loop and update it within the loop:

>>> a = 5
>>> while a < 100:
...    print a
...    a = a+10
...
5
15
25
35
45
55
65
75
85
95

But you need to be careful that the loop doesn’t go on forever!  If the program is not responsive try Ctrl-C to stop it:

>>> while True:
...     pass
...

^CTraceback (most recent call last):
 File "<stdin>", line 1, in <module>
KeyboardInterrupt

if it wasn’t for the range…

So, let’s combine some of what we’ve learnt from the past few tutorials.  Let’s say we wanted to print all the even numbers from 0 to 20.  We know how to get these numbers by using range(21), but this gives us all the numbers from 0 to 20.  We just want the even ones.

Even numbers are those which have a remainder of 0 when you divide them by two.  Python has  a special operator (called %) which does just this:

>>> 1%4
1
>>> 2%4
2
>>> 3%4
3
>>> 4%4
0
>>> 5%4
1

So for example: 1%4 means “what is the remainder when you divide 1 by 4” (it’s 1).   To test whether a number is even we can see whether this remainder is 0 when the number is divided by 2:

>>> 1%2
1
>>> 2%2
0
>>> 3%2
1
>>> 4%2
0

One is odd, two is even etc.

So to print the even numbers from 0 to 20 we:

get the numbers from 0 to 20 from  a range statement

we use a “for” loop to run over each of the numbers in that range

for each of those numbers we test to see if it is even – and if it is

print the number:

>>> for i in range(21):
...   if i%2 == 0:
...      print i
...
0
2
4
6
8
10
12
14
16
18
20

Please notice that there are three levels of indentation here.  That’s significant because each indicates a code block which relates to the previous statement.

Now try to print out the numbers divisible by 10 between 0 and 100.

And then try to print out the numbers in the sequence 5, 15, 25… up to 95.  This is harder, but you can do it with what’s on this page.

Later we’ll learn that you can do much of this with just range() alone, but that’s for another day.

If only

Sometimes life presents you with challenges.   Some are large and some are small.  How you decide to deal with them has consequences.  Those consequences flow from your decision.  If I eat all my lollies now, I won’t have any after dinner.   If I annoy my friend Ben he won’t invite me over anymore.  If I don’t keep my hands to myself I don’t get the DS.  If I don’t let the dog out when I get up, I don’t get to play computer games. When these conditions are coupled with consequences, they can guide our lives.  Programs are the same.  Not exactly the same, but similar.

Within a program you can direct its flow by using “if”.  The if statement has a particular structure which needs to be followed (called its “syntax”).  The structure of if is as follows:

if :

Don’t worry about the stuff in angle brackets <> at the moment.  The important thing is that the first line starts with “if” and ends with a colon “:”.  No colon and Python will get upset. Further, the code that follows the colon is indented.  That’s important!

>>> time = '5PM'
>>> if time == '5PM':
...    print 'Time to play the computer!!'
...
Time to play the computer!!

So, working through this code… First the string ‘5PM’ is put in the bucket that we’ve called ‘time’.  Then, we’ve compared to see if the time is ‘5PM’.  If it is, then a message is printed (‘Time to play the computer!!!’).

Notice also that we said “time ==”, not “time =”.  That’s because ‘=’ and ‘==’ mean different things to Python.  When you write ‘=’ you are saying to Python, “take the thing on the right and put it in the thing on the left”.  However, when you write ‘==’ you are saying to Python, “these two things are equal – true or false?”

>>> time = '6PM'
>>> if time == '5PM':
...    print 'Time to play the computer!!'
...
>>>

If the condition (in this case: does time equal ‘5pm’) is not true, then the code is not processed.  Here we assigned a different time (actually string)  to time.  When we ran through the condition, Python said “well, that’s false now” so the code didn’t execute.

You can use other sorts of comparisons (like > and <)  in the condition.  They won’t work well in the examples above because time is a string (type), not actually a representation of time.   So if things like this work:

>>> time = '6PM'
>>> if time > '5PM':
...    print 'Time to play the computer!!'
...
Time to play the computer!!

it’s only because of blind luck.  This really won’t work:

>>> time = '6AM'
>>> if time > '5PM':
...    print 'Time to play the computer!!'
...
Time to play the computer!!

In this case, it’s a failure because the message printed when it shouldn’t have (6AM is not later than 5PM, but it printed anyway).  It’s doing this because Python is comparing them as if they were strings (which they are) not as if they were times.  Python is able to compare them as times, but that’s not something we’ll cover here.

We can compare things in lots of ways:

a< b – a is less than b: true or false?

>>> a,b = 1,2
>>> if a < b:
...   print "a is less than b"
...
a is less than b

Did you notice the sneaky way we did two assignments there?  Instead of a=1 and b=2 we said a,b = 1,2.  You can do this too.  Also pay attention to the fact that both a and b are integers, not strings – they have no quotation marks.

a> b – a is greater (or more) than b: true or false?

>>> if a > b:
...   print "a is greater than b"
...

Nothing printed because a is 1, which is less than b, which is 2.

not – this means “not” so if you have another comparison you can negate it:

not a > b  – it is not the case that a is greater than b: true or false? (think it through, it does make sense)

>>> if not a > b:
...   print "it is not true that a is greater than b"
...
it is not true that a is greater than b

a != b the ! (sometimes called a “shriek!” or a “bang!”) here means “not” (strangely this is just “!=”, not “!==” which given what we said about Python and “==” above would make more sense, but also take more typing.  Perhaps Python is just a bit schizophrenic?).   The translation of this is: a is not equal to b: true or false?

>>> if a != b:
...    print "They are not equal"
...
They are not equal

a <= b – a is less than or equal to b: true or false? The other way around (=<) won’t work:

>>> if a <= b: ...   print "a is less than or equal to b" ... a is less than or equal to b >>> if a =< b:
 File "", line 1
 if a =< b:
 ^
SyntaxError: invalid syntax

a >= b – a is greater than or equal to b: true or false?

You can also connect a number of conditions together using “and” and “or”:

>>> if a==1 and b== 2:
...    print 'a equals 1 AND b equals 2'
...
a equals 1 AND b equals 2

>>> if a == 1 or b ==5:
...    print "a equals 1 or b equals 5 (or both)"
...
a equals 1 or b equals 5 (or both)

Try some different combinations.

You can even let Python do some calculations before it compares things:

>>> if 5-1 == 3+1:
...   print "four equals four"
...
four equals four

In fact, Python’s methods of evaluating the conditions you pass it are more many and varied than this.  We may meet them later.  But for the time being note this:

>>> if 1:
...   print "1 is true!"
...
1 is true!
>>> if 0:
...   print "But 0 isn't"
...

Ponder it.

Interacting with raw_input

Python also has a feature which lets you interact with the outside world to get input.  The raw_input() function waits for the user to type some input and press return.  It then gets whatever was typed.

>>> raw_input()
some input
'some input'

It’s not obvious what is going on here.  When you hit return at the end of the first line, a blank line appears.  You type what you want into the line and when you hit enter Python gets the line and (in this case, because we’re in an interactive window) prints what it got.  Do it yourself to see, because the text doesn’t show how it happens.

We might have instead assigned the input to a placeholder:

>>> a = raw_input()
some more input, where is it all going?
>>> # note it hasn't printed anything?
...
>>> a
'some more input, where is it all going?'

In a program this allows us to get information into the program, which might come in handy for maths (aka ‘math’) homework:

>>> a=raw_input()
45
>>> b=raw_input()
23
>>> a+b
'4523'

Ooops!  The input that Python is getting is returned to us as strings.  And strings have a funny form of plus called concatenation.  We can use what we learned earlier to push this input into numbers:

>>> int(a)+int(b)
68

That’s what we were looking for (notice that there are no quotes around 68, because it’s a number now).

We are working in the interactive shell at the moment so the context of what is going on is clear.  However, when running a python program “in the wild” there won’t be that context.  You can add some information for the user if you put a message inside the raw_input brackets.  The message must be a string – so it must have quotes or it must be a variable which holds something which has quotes:

>>> a=raw_input('Enter your number here: ')
Enter your number here: 34

Can you see the string has been reproduced in front of the place you type?  This is called a ‘prompt’ because it prompts you to enter something.

Range and For

>>> range(5)
[0, 1, 2, 3, 4]
>>> for i in range(5):
...   print i
...
0
1
2
3
4
>>>

Strings are not numbers

>>> a='5'
>>> a+1
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
>>> a=5
>>> a+1
6
>>>

Notice that when the number is in quotes ‘5’ it’s not a number anymore (it’s called  a string).   When it doesn’t have quotes it’s treated as a number which can be added.

In fact, the issue isn’t that you can add numbers, but not strings, it’s that ‘5’  and 5 are different ‘types’.  ‘5’ is a string but 5 (no quotes) is an integer.  The two ‘types’ don’t mix. But you can still ‘add’ two strings together:

>>> a='5'
>>> a+'1'
'51'
>>>

Note here though that ‘1’ is not a number, it’s a string (because it has quotation marks around it).  Note also that it’s a sort of addition, but not as we’re used to it.   It’s called ‘concatenation’ which sort of means sticking together end to end.

Try it with some other strings:

>>> a = 'hello'
>>> b = 'there'
>>> a+b
'hellothere'
>>> # hmmmm. maybe I need to add a space?
...
>>> a+' '+b
'hello there'

If you have a string, but want to have a number, Python has a way of trying to push it into a number, called int(). You put the string in the brackets and int will give you a number back if it can.  Sometimes it can’t.

>>> int('5')
5
>>> int('hi there')
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'hi there'
>>>

So, if you ever know you’ve got a number trapped inside a string, you can sometimes still get it out if you remember the int() function.

>>> a = '5'
>>> a+1
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects
>>> int(a)+1
6
>>> a
'5'
>>>

See, in this example, that a is still a string, but we’ve managed to push its number out of it.

This is very important as you will often receive numbers hidden in strings and will need to convert them before you can do anything with  them.

Hello world!

(Works for both Python 2.7 and Python 3)
Your first program, for reasons lost to the mists of time, must always be a “hello world!”  If you don’t do it, something bad will happen to you (or one of your friends, relatives, distant relatives, acquaintances or some other citizen of your country).  Believe me, it is scary.

So, to avoid such a horrible fate, repeat after me:

>>> print('hello world!')
hello world!
>>>

Phew! We’re done.    You should notice that the print statement here did something – it printed the letters between the ” (inverted commas) and it printed them on the next line.  Try it for yourself, but remember to press the enter key at the end otherwise nothing will happen.

Congratulations, you’re now a programmer (in training).