Baby Steps with Our Text Editor


Second Interviewer     I didn’t really call you Eddie-baby, did I, sweetie?
Ross     Don’t call me sweetie!!
Second Interviewer     Can I call you sugar plum?

In this tutorial we’re going to start using the power of the text editor that I’ve been badgering you about over the last few tutorials.  We are going to go back to the earlier tutorials and resurrect some of the code there.

Exercise:
Create a new document with your text editor
Save the document to a file called trivia.py  You need to be careful that your text editor doesn’t add ‘.txt’ to the end of the file’s name as some have the habit of doing.
copy the following function into the file (originally found here, with some comments dropped):

def askQuestion(questionList):
    question = questionList[0]
    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[0]
    answers[0] = 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...'

save the changes you’ve just made to the file
It should look something like this (notice the code highlighting in Kate.  A basic text editor like NotePad won’t do this highlighting for you):

now, start a python prompt from the command line and import the module: (note: for this to work you must be in the same directory as the trivia.py file when you start the Python prompt Google “cd change directory” if you have problems)

>>> import trivia
>>> dir(trivia)
['__builtins__', '__doc__', '__file__', '__name__', 'askQuestion']

Can you see that we’ve imported our trivia file at the Python prompt (note: not import trivia.py, Python automatically adds the .py when it’s looking for the module).  We’ve used introspection to see its attributes and methods – you can see the askQuestion() function there.  We can’t actually ask a question at the moment, because we don’t have a question to ask, so let’s recover those we were storing that pickle file from earlier tutes:

>>> import cPickle
>>> filename = 'p4kTriviaQuestions.txt'
>>> fileObject = open(filename,'r')
>>> triviaQuestions = cPickle.load(fileObject)
>>> fileObject.close()
>>> triviaQuestions
[['Who expects the Spanish Inquisition?', 'Nobody', 'Eric the Hallibut', 'An unladen swallow', 'Brian', 'Me!'], ['What is the air-speed velocity of an unladen swallow?', 'What do you mean? African or European swallow?', '10 m/s', '14.4 m/s', '23.6 m/s'], ['Is this the right room for an argument?', "I've told you once", 'No', 'Down the hall, first on the left', 'Yes']]
>>> triviaQuestions[0]
['Who expects the Spanish Inquisition?', 'Nobody', 'Eric the Hallibut', 'An unladen swallow', 'Brian', 'Me!']

Now, let’s get a single question, because that’s what the askQuestion() function is expecting:

>>> question = triviaQuestions[0]

So, in theory we should be ready to run this through our askQuestion() function.  Let’s try it:

>>> trivia.askQuestion(question)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "trivia.py", line 6, in askQuestion
    correctAnswer = random.randint(0, numberOfAnswers)
NameError: global name 'random' is not defined

Uh-oh, it didn’t work because we haven’t imported the random module. Gosh darn it.  Let’s add that at the start of our file so the first couple of lines now look like this:

import random

def askQuestion(questionList):
    question = questionList[0]
    answers = questionList[1:]

Now we go back to the Python command line and import it again:

>>> import trivia
>>> trivia.askQuestion(question)

Unfortunately, this also fails exactly as it did before – even though we know we’ve imported the random module.  The reason is that import only happens once.  If you change a module, then import it a second time, it doesn’t do anything.  However, we can update a module by using the reload() function (you can also exit the Python prompt, restart Python and then import, but reload() is easier).  Once we do that, things start working as we expect them to:

>>> reload(trivia)
<module 'trivia' from 'trivia.py'>
>>> trivia.askQuestion(question)
Who expects the Spanish Inquisition?
0 .  An unladen swallow
1 .  Eric the Hallibut
2 .  Nobody
3 .  Brian
4 .  Me!
Enter the number of the correct answer: 2
Correct! Hooray!

So, some things to notice:
* when we imported our module, we imported the name of the file less the ‘.py’
* on import it created a ‘namespace’ called trivia.  The function we defined was in the trivia namespace and we accessed it using a dot: trivia.askQuestion().  Had we just typed in askQuestion() the computer would not know what we are talking about:

>>> askQuestion(question)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'askQuestion' is not defined

We can import functions individually using a different syntax and, if we do, the function’s name is present in the base namespace and it can be called directly:

>>> from trivia import askQuestion
>>> askQuestion(question)

However, doing this is frowned upon because it ‘pollutes’ the namespace.  In particular, you may end up with collisions between functions imported into the namespace.  So if you have two modules, each with the a function of the same name, but of different effect the interpreter would have a problem if you from imported both of them.  Try to avoid using the import from syntax at least until you’re familiar with why it’s a problem.

Something our function is missing is a doc string.  Let’s add one.  Go back to your text editor and edit the function so now it reads:

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[0]
    answers = questionList[1:]

Save the file
Go to the Python prompt and reload() the module, then look at the docstring:

>>> reload(trivia)
<module 'trivia' from 'trivia.py'>
>>> print trivia.askQuestion.__doc__

        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

Now, the important thing to note is that we were able to change part of the function without retyping all of it.  Going forward we will be able to extend this module by adding bits to it.

Homework:
Create a docstring for the trivia module itself, describing what the trivia module will be doing (make a list of the things you think it should eventually do in order to make a trivia game).  Work out where the docstring needs to go in trivia.py.  Save the file, then reload the module from the Python prompt and print the docstring out.

Homework:
Think about what part of the Python language we might use to ask all of the questions stored in the pickle file, rather than just the first one.

2 Responses to Baby Steps with Our Text Editor

  1. Pingback: A Functioning Stand Alone Python Program « Python Tutorials for Kids 8+

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.