Introduction to Python Programming - Session 5 Catchup
This article was for Hacksoc Nottingham, while I was the 2015-2016 General Secretary.
Lists, Glorious Lists
As we briefly touched upon last session, lists are a great way to work with multiple variables, without having separate variables assigned for each one. We're also able to store different bits of data in them, such as
mixed_list in the below example:
names = ["Rich", "Jamie", "Luke", "Harry", "Gregor"] hair = ["Brown", "Red", "Ginger", "Green", "Brown"] mixed_list = [1, "hello", 1.235, 12*10, ("Hellooooo", 123)] print names print hair print mixed_list for name in names: print "Our new name is", name for hair_colour in hair: print "Our new hair colour is", hair_colour
Remember from last time that the syntax for
lists is using square brackets,
[ to start a list,
] to end it, and commas to separate each individual value.
In the first set of
In the second set of print statements, we are going to iterate through each value of the list. This is called a
for loop, and the syntax makes it much easier for us to go through each element individually. The value of
name is reset at each point in the loop with the value of the next element in the list.
Note: I may use the term
array in place of the term
list. For these tutorials, they are interchangeable - so please don't be confused if I use either.
- Create a for loop that iterates through
mixed_listand prints out each element.
Accessing the List Elements Directly
Luckily we don't always have to iterate through the whole list, but we can instead access the element at a given position.
Before we look at how, one point to always remember about working with
lists is that they are zero-indexed (zero-indexed: instead of starting counting at one, we start at zero). As with all modern programming languages, we start counting from zero; there is no point wasting space in the computer's memory to start counting from one like 'normal'.
Therefore, if we wanted to access elements of the list, we would do something like:
names = ["Rich", "Jamie", "Luke", "Harry", "Gregor"] print "The first name is", names print "The second name is", names print "The fourth name is", names
Note that the syntax follows that of how we create lists - we use the square brackets.
Because of the way that we can access individual elements, we can also use a loop counter inside our for loop to access the elements directly, like so:
names = ["Rich", "Jamie", "Luke", "Harry", "Gregor"] hair = ["Brown", "Red", "Ginger", "Green", "Brown"] mixed_list = [1, "hello", 1.235, 12*10, ("Hellooooo", 123)] print names print hair print mixed_list loop_counter = 0 print names for name in names: hair_colour = hair[loop_counter] print "Hello my name is", name, "and I have", hair_colour, "hair" loop_counter = loop_counter + 1
As we can see here, we're iterating through the
names list, and using our
loop_counter to access the elements of
hair_colour. But this isn't ideal as we're mixing accessing styles, which does not make sense. To clean this up, we're going to access both through the loop counter, which will be generated with the Python function
range is a really useful function which, given an integer, creates a list of the numbers up to, but not including, the given integer. For instance:
>>> range(1)  >>> range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> range(30) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] >>> range(3) [0, 1, 2]
Therefore, we can then use the following code to iterate through both lists. The previous version is shown as a comparison.
names = ["Rich", "Jamie", "Luke", "Harry", "Gregor"] hair = ["Brown", "Red", "Ginger", "Green", "Brown"] mixed_list = [1, "hello", 1.235, 12*10, ("Hellooooo", 123)] print names print hair print mixed_list loop_counter = 0 for name in names: hair_colour = hair[loop_counter] print "Hello my name is", name, "and I have", hair_colour, "hair" loop_counter += 1 for loop_counter in range(5): name = names[loop_counter] hair_colour = hair[loop_counter] print "Hello my name is", name, "and my hair is", hair_colour, "colour"
We use the temporary variables in the loop for readability, but we could just as simply used the list access syntax instead.
+= shorthand for incrementing our
Magic Numbers are the Devil
In the above code, we've hardcoded in the number
5 as the length of the list - this is a terrible idea, because every time we increase the size of the list, we need to remember to update the value stored there. Additionally, when reading back over the code we may not instantly work out what the
5 refers to. This is referred to as a magic number (magic number: "unique values with unexplained meaning") and using magic numbers is very bad when maintaining code, and should instead be replaced by a variable which explains what it means, for instance:
... LENGTH_OF_LIST = 5 for loop_counter in range(LENGTH_OF_LIST): name = names[loop_counter] hair_colour = hair[loop_counter] print "Hello my name is", name, "and my hair is", hair_colour, "colour"
As we can see, the choice of variable name makes it very easy to understand what the value indicates. However, we will still need to remember to update it as soon as we change the list. Therefore, we will instead use the
len function, which gives us the length of a given
for loop_counter in range(len(names)): name = names[loop_counter] hair_colour = hair[loop_counter] print "Hello my name is", name, "and my hair is", hair_colour, "colour"
This version will then let us adapt to a changing
list length much more easily, and means we don't have to update our variable when the
list changes. While this may seem trivial in this example, when we get to a larger program, we will have potentially tens of lists and variables to update, and it'll get a bit too much to remember every time.
Reducing the Number of Lists
As we can see in our code, the
hair lists are very tightly coupled - we will always have an entry in one for the other. Therefore, having two variables stored is not ideal. To get around this, we will use tuples, which we again briefly touched on last session, which are a data structure that allows us to store multiple values (be it two or two hundred) i.e. pairs of coordinates. In our example, we will pair a person's name with their hair colour, as such:
people = [('Rich', 'Brown'), ('Jamie', 'Red'), ('Luke', 'Ginger'), ('Harry', 'Green'), ('Gregor', 'Brown')] print people PERSON_LIST_NAME = 0 number_of_people = len(people) for loop_counter in range(number_of_people): (name, hair_colour) = people[loop_counter] print "Hello my name is", name, "and my hair is", hair_colour, "colour" person = people[loop_counter] print "Hello, this is: ", person print "Hello, this is", person[PERSON_LIST_NAME], "with hair colour", person
There are a few things we're doing here. Firstly, the syntax for a tuple is the same as a pair of coordinates - parentheses around the values, and commas separating them.
You will see there are two methods of accessing the tuple values - firstly, we capture the values in the same way as we get multiple return types from a function, just with the added parentheses. Secondly, we index into the tuple, in the same syntax as we index into
lists. Both methods work identically, and are purely down to preference.
Here we had a break for you to implement the following:
- Take a name from the user
- Check the
listof people for their name
- When you find their name, print out their hair colour
- Bonus points: Error checking! What if there isn't someone by that name?
The answer is intentionally not given, so you have to work to implement it yourself.
PyZork: The All-Star Spelling Conundrum
Name taken from Video Game Name Generator.
At the end of the session, we started to create a text-based adventure game. This is our last exercise before we move to Flask, and aims to summarise all the work we've done so far.
You can see below the initial skeleton (pun intended) for the game:
from sys import exit def get_str_from_user(prompt): input = raw_input(prompt) input = str(input) return input def dead(why): print "You dead." print "..." print "How?", why exit(0) def zombies(): dead("You get eaten by zombies") def start(): print "You wake up in your room" print "You look down and notice a dirty rag on your foot" print "What will you do?" isInRoom = True while isInRoom: # if # .. in print "" print "'pickup' the rag" print "turn on the 'lights'" print "go to 'sleep'" print "look for an 'exit'" inp = get_str_from_user("What will you do?") if inp == "pickup": print "You gain one rag. Lucky you" elif inp == "lights": print "Well done /s" room_lights() elif inp == "sleep": zombies() elif inp == "exit": print "You're in the dark, you can't find an exit" def room_lights(): print "You can see a door" print "What will you do?" isInRoom = True while isInRoom: print "" print "'exit'" print "go to 'sleep'" inp = get_str_from_user("What will you do?") if inp == "exit": # TODO where next? pass start()
Yes, you've been set homework this week! It's to design and start to implement your own game, following the structure above. This will you a chance to get ahead, and make the session a lot easier, as we'll be carrying on your work and finishing the game. Once finished, we'll play them live!