May 17, 2011 1 Comment
Interviewer: The Magna Carta – was it a document signed at Runnymede in 1215 by King John pledging independence to the English barons, or was it a piece of chewing gum on a bedspread in Dorset? The latter idea is the brainchild of a man new to the field of historical research. Mr Badger, why – why are you on this programme?
[Pull back to show Mr Badger. He wears a flat cap and has a Scots accent.]
Badger: Well, I think I can answer this question most successfully in mime. (mimes incomprehensibly)
In the last tutorial we created a file using our text editor and saved a function to it. This file was called trivia.py and in it was the module “trivia”. We then started Python in a console and import()ed the trivia module. Once imported, it created a “namespace” and we could access the askQuestion() function from within the trivia namespace by using a dot – trivia.askQuestion(). In order for the module to work properly we had to include an import statement within the module itself so that everything that the module relied upon was imported within the module. We then manually loaded our data from a pickle file we created and, manually, ran the askQuestion() function on the first question in our data store. Finally we added docstrings to the function and the module.
In this tutorial we’re going to try to do much the same thing again, but without using the Python interpreter. That is, we will need to take the things we did in the interpreter and implement them in our trivia.py file. We will have a functioning (although still quite simple) stand alone Python program.
To start, open up trivia.py in your text editor.
Looking at the last tute, we can see that we used the cPickle module, so that will need to be imported. We then opened the existing pickle file, and loaded the stored pickle.
So, first, edit the file to add import cPickle after import random:
import random import cPickle
Next, let’s add a ‘constant' to store our the filename of our pickle file. Constants are not really all that constant in Python, in that Python will let us change their value later if we choose. However, by convention, if you name a variable with all caps, then it is assigned a value only once (usually at the start of the program). Add this after the imports but before the first definition:
QUESTIONS_FILENAME = 'p4kTriviaQuestions.txt'
Now, we add some code to load the questions into an array. In theory, this code could go anywhere in the file after the variable QUESTIONS_FILENAME has been given a value. However, it’s better if we put it at the end of the file after the function definition. It should not be indented (remember that Python identifies code blocks by indentation):
fileObject = open(QUESTIONS_FILENAME,'r') # note: 'r' for read questionsList = cPickle.load(fileObject) # load the questions fileObject.close() # finished reading, so close the file
If you go back through the previous tutes (eg the previous tute) you’ll see that this is the same code we used in the interpreter. This shouldn’t be surprising because Python runs the code in this program as if it was being typed into the interpreter. Now that the questions have been loaded, let’s iterate through each of them, asking them in turn:
for question in questionsList: askQuestion(question) # note, that because we're within the module we can just # use the function's name directly without being qualified by the # trivia namespace. If you put trivia.askQuestion, the code would not work.
Save the file [see below for what the code should look like], then go to a console/command line – ie the thing from which you have run the Python interpreter in earlier tutes. Do not run the interpreter itself. Rather, we run the file by typing the following and pressing the ‘enter’ or ‘return’ key:
> python trivia.py Who expects the Spanish Inquisition? 0 . Brian 1 . Eric the Hallibut 2 . An unladen swallow 3 . Nobody 4 . Me! Enter the number of the correct answer: 3 Correct! Hooray! What is the air-speed velocity of an unladen swallow? 0 . 23.6 m/s 1 . 10 m/s 2 . 14.4 m/s 3 . What do you mean? African or European swallow? Enter the number of the correct answer: 3 Correct! Hooray! Is this the right room for an argument? 0 . Down the hall, first on the left 1 . No 2 . I've told you once 3 . Yes Enter the number of the correct answer: 2 Correct! Hooray!
Note that this is from the command line – not the Python interpreter. You can tell because there is only one > (you might have something different as your > thingamy) where the interpreter has three >>>
As you can see the two lines:
for question in questionsList: askQuestion(question)
Cause the program it iterate through each element in the list questionsList and, for each element, to call the askQuestion() function with that element as a parameter. If you remember, each of these elements is, itself, a list.
Homework: think about how we would need to change this program to keep track of the player’s score
Bonus points: actually change the program so that it keeps track of the player’s score and prints out the score after all questions have been asked.
1. Technically, in the code:
QUESTIONS_FILENAME = ‘p4kTriviaQuestions.txt’,
QUESTIONS_FILENAME is a variable, and ‘p4kTriviaQuestions.txt’ is the constant. However, in the text I’m referring to the variable as if it was the constant.
2. Actually, when you run a program from the command line Python reads the whole file and pre-compiles it first (or uses an existing pre-compiled version if you haven’t changed the file) then runs the pre-compiled version. Have a look for file called trivia.pyc in your directory.
The complete file should look like this:
''' The place for a doctring which was an exercise for you to complete in the previous tute. ''' import random import cPickle QUESTIONS_FILENAME = 'p4kTriviaQuestions.txt' def askQuestion(questionList): ''' Given a question in a form of a list, with the first entry being the question, the next entry being the correct answer to the question and one or more other entries, each of which is an incorrect answer, pose the question, randomising the answers and test whether the answer is correct ''' question = questionList answers = questionList[1:] numberOfAnswers = len(answers)-1 # -1 because the first entry in the list is number 0 correctAnswer = random.randint(0, numberOfAnswers) # choose an answer at random # then swap it with the correct answer spam = answers[correctAnswer] answers[correctAnswer] = answers answers = spam print question for i in range(len(answers)): print i,'. ',answers[i] answer = raw_input('Enter the number of the correct answer: ') if answer == str(correctAnswer): print 'Correct! Hooray!' else: print 'Wrong...' fileObject = open(QUESTIONS_FILENAME,'r') # note: 'r' for read questionsList = cPickle.load(fileObject) # load the questions fileObject.close() # finished reading, so close the file for question in questionsList: askQuestion(question) # note, that because we're within the module we can just # use the function's name directly without being qualified by the # trivia namespace.