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)

Follow

Get every new post delivered to your Inbox.

Join 72 other followers