Using More Built-in Python Types

Using More Built-in Python Types

Beside strings and numbers, Python provides four other important basic types: tuples, lists, sets, and dictionaries. These four types have a lot in common because they all allow you to group more than one item of data together under one name. Each one also gives you the capability to search through them because of that grouping. These groupings are indicated by the presence of enclosing parentheses (), square brackets [], and curly braces {}.

WARNING: When you write a program, or read someone else’s program, it is important to pay attention to the type of enclosing braces when you see groupings of elements. The differences among {}, [], and () are important.

Tuples—Unchanging Sequences of Data

In Chapters 1 and 2, you saw tuples (rhymes with supple) being used when you wanted to assign values to match more than one format specifier in a string. Tuples are a sequence of values, each one accessible individually, and a tuple is a basic type in Python. You can recognize tuples when they are created because they’re surrounded by parentheses:

>>> print("A %s %s %s %s" % ("string", "filled", "by a", "tuple"))

A string filled by a tuple

Try It Out Creating and Using a Tuple

Tuples contain references to data such as strings and numbers. However, even though they refer to data, they can be given names just like any other kind of data:

>>> filler = ("string", "filled", "by a", "tuple")

>>> print("A %s %s %s %s" % ("string", "filled", "by a", "tuple"))

A string filled by a tuple

Note that you can also print out the values in the tuple by simply calling upon it in the print() function. Try the following code and observe the results:

>>> filler = ("string", "filled", "by a", "tuple")

>>> print(filler)

(‘string’, ‘filled’, ‘by a ‘, ‘tuple’)

As you can see, the four parts that made up the tuple were returned. This technique is useful if you ever want to see the individual parts that make up your tuple.

How It Works

You can see in the example that filler is treated exactly as though its data — the tuple with strings—were present and being used by the string to fill in its format specifiers because the tuple was treated exactly as though you had typed in a sequence to satisfy the format specification.

You can access a single value inside of a tuple. The value referred to by each element can be accessed directly by using the dereference feature of the language. With tuples, you dereference the value by placing square brackets after the name of the tuple, counting from zero to the element that you’re accessing. Therefore, the first element is 0, the second element is 1, the third element is 2, and so on until you reach the last element in the tuple:

>>> a = ("first", "second", "third")

>>> print("The first element of the tuple is %s" % a[0])

The first element of the tuple is first

>>> print("The second element of the tuple is %s" % a[1])

The second element of the tuple is second

>>> print("The third element of the tuple is %s" % a[2])

The third element of the tuple is third

A tuple keeps track of how many elements it contains, and it can tell you when you ask it by using the built-in function len:

>>> print("%d" % len(a))

3

This returns the number of elements in the tuple (in this case 3), so you need to remember that the len function starts counting at 1, but when you access your tuple, because tuples are counted starting from zero, you must stop accessing at one less than the number returned by len:

>>> print(a[len(a) – 1])

Third

You can also have one element of a tuple refer to an entirely different tuple. In other words, you can create layers of tuples:

>>> b = (a, "b’s second element")

>>>print(b)

((‘first’, ‘second’, ‘third’), "b’s second element")

Now you can access the elements of the tuple a by adding another set of brackets after the first one, and the method for accessing the second element is no different from accessing the first — you just add another set of square brackets.

Try It Out Accessing a Tuple Through Another Tuple

Re-create the a and b tuples so that you can look at how this works. When you have these layers of sequences, they are sometimes referred to as multidimensional because there are two layers that can be visualized as going down and across, like a two-dimensional grid for graph paper or a spreadsheet. Adding another one can be thought of as being three-dimensional, like a stack of blocks. Beyond that, though, visualizing this can give you a headache, and it’s better to look at it as layers of data.

>>> a = ("first", "second", "third")

>>> b = (a, "b’s second element")

>>> print("%s" %b[1])

b’s second element

>>> print("%s" % b[0][0])

first

>>> print("%s" % b[0][1])

second

>>> print("%s" % b[0][2])

third

How It Works

In each case, the code works exactly as though you had followed the reference in the first element of the tuple named b and then followed the references for each value in the second layer tuple (what originally came from the tuple a). It’s as though you had done the following:

>>> a = ("first", "second", "third")

>>> b = (a, "b’s second element")

>>> layer2 = b[0]

>>> print(layer2[0])

‘first’

>>> print(layer2[1])

‘second’

>>> print(layer2[2])

‘third’

Note that tuples have one oddity when they are created. To create a tuple with one element, you absolutely have to follow that one element with a comma:

>>> single_element_tuple = ("the sole element",)

Doing otherwise will result in the creation of a string, and that could be confusing when you try to access it later.

A tuple can have any kind of data in it, but after you’ve created one it can’t be changed. It is immutable, and in Python this is true for a few types (for instance, strings are immutable after they are created; and operations on them that look like they change them actually create new strings).

Tuples are immutable because they are supposed to be used for ordered groups of things that will not be changed while you’re using them. Trying to change anything in them will cause Python to complain with an error, similar to the errors you were shown at the end of Chapter 2:

>>> a[1] = 3

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

TypeError: object does not support item assignment

>>> print("%s" % a[1])

second

You can see that the error Python returns when you try to assign a value to an element in the tuple is a TypeError, which means that this type doesn’t support the operation you asked it to do (that’s what the equals sign does — it asks the tuple to perform an action). In this case, you were trying to get the second element in a to refer to an integer, the number 3, but that’s not going to happen. Instead, a remains unchanged.

An unrelated error will happen if you try to refer to an element in a tuple that doesn’t exist. If you try to refer to the fourth element in a, you will get an error (remember that because tuples start counting their elements at zero, the fourth element would be referenced using the number three):

>>> a[3]

Traceback (most recent call last):

  File "<pyshell#27>", line 1, in <module>

  a[3]

IndexError: tuple index out of range

Note that this is an IndexError and that the explanation of the error is provided (although it doesn’t tell you the index value that was out of range, you do know that you tried to access an element using an index value that doesn’t exist in the tuple). To fix this in a program, you would have to find out what value you were trying to access and how many elements were in the tuple. Python makes finding these errors relatively simple compared to many other languages that will fail silently.

Lists—Changeable Sequences of Data

Lists, like tuples, are sequences that contain elements referenced starting at zero. Lists are created by using square brackets:

>>> breakfast = [ "coffee", "tea", "toast", "egg" ]

Try It Out Viewing the Elements of a List

The individual elements of a list can be accessed in the same way as tuples. Like tuples, the elements in a list are referenced starting at 0 and are accessed in the same order from zero until the end:

>>> count = 0

>>> print("Today’s breakfast is %s" % breakfast[count])

Today’s breakfast is coffee

>>> count = 1

>>> print("Today’s breakfast is %s" % breakfast[count])

Today’s breakfast is tea

>>> count = 2

>>> print("Today’s breakfast is %s" % breakfast[count])

Today’s breakfast is toast

>>> count = 3

>>> print("Today’s breakfast is %s" % breakfast[count])

Today’s breakfast is egg

How It Works

When you are accessing more than one element of a list, one after the other, it is essential to use a name to hold the value of the numbered position where you are in the list. In simple examples like this, you should do it to get used to the practice, but in practice, you will always do this. Most often, this is done in a loop to view every element in a sequence (see Chapter 4 for more about loops).

Here, you’re manually doing the work of increasing the value referred to by count to go through each element in the breakfast list to pull out the special for four days of the week. Because you’re increasing the count, whatever number is referred to by count is the element number in the breakfast list that is accessed.

The primary difference in using a list versus using a tuple is that a list can be modified after it has been created. The list can be changed at any time:

>>> breakfast[count] = "sausages"

>>> print("Today’s breakfast is %s" % breakfast[count])

Today’s breakfast is sausages

You don’t just have to change elements that already exist in the list, you can also add elements to the list as you need them. You can add elements at the end by using the append method that is built in to the list type. Using append enables you to append exactly one item to the end of a list:

>>> breakfast.append("waffles")

>>> count = 4

>>> print ("Today’s breakfast is %s" % breakfast[count])

Today’s breakfast is waffles

If you want to add more than one item to the end of a list — for instance, the contents of a tuple or of another list — you can use the extend method to append the contents of a list all at once. The list isn’t included as one item in one slot; each element is copied from one list to the other: 

>>> breakfast.extend(["juice", "decaf", "oatmeal"])

>>> print(breakfast)

[‘coffee’, ‘tea’, ‘toast’, ‘egg’, ‘waffle’, ‘juice’, ‘decaf’, ‘oatmeal’]

As with tuples, you can’t ask for an element beyond the end of a list, but the error message is slightly different from a tuple because the error will tell you that it’s a list index that’s out of range, instead of a tuple index that’s out of range:

>>> count = 8

>>> print("Today’s breakfast is %s" % breakfast[count])

Traceback (most recent call last):

  File "<pyshell#18>", line 1, in <module>

  print("Today’s breakfast is %s" % breakfast[count])

IndexError: list index out of range

The length of an array can also be determined by using the len function. Just like tuples, lengths start at one, whereas the first element of a list starts at zero. It’s important to always remember this.

Dictionaries—Groupings of Data Indexed by Name

A dictionary is similar to lists and tuples. It’s another type of container for a group of data. However, whereas tuples and lists are indexed by their numeric order, dictionaries are indexed by names that you choose. These names can be letters, numbers, strings, or symbols—whatever suits you.

Try It Out Making a Dictionary

Dictionaries are created using the curly braces. To start with, you can create the simplest dictionary, which is an empty dictionary, and populate it using names and values that you specify one per line:

>>> menus_specials = {}

>>> menus_specials["breakfast"] = "Canadian ham"

>>> menus_specials["lunch"] = "tuna surprise"

>>> menus_specials["dinner"] = "Cheeseburger Deluxe"

How It Works

When you first assign to menus_specials, you’re creating an empty dictionary with the curly braces. Once the dictionary is defined and referenced by the name, you may start to use this style of specifying the name that you want to be the index as the value inside of the square brackets, and the values that will be referenced through that index are on the right side of the equals sign. Because they’re indexed by names that you choose, you can use this form to assign indexes and values to the contents of any dictionary that’s already been defined.

When you’re using dictionaries, the indexes and values have special names. Index names in dictionaries are called keys, and the values are called, well, values. To create a fully specified (or you can think of it as a completely formed) dictionary — one with keys and values assigned at the outset — you have to specify each key and its corresponding value, separated by a colon, between the curly braces. For example, a different day’s specials could be defined all at once:

>>> menu_specials = {"breakfast" : "sausage and eggs",

…     "lunch" : "split pea soup and garlic bread",

…     "dinner": "2 hot dogs and onion rings"}

To print out all of the keys and values in a dictionary, simply place the name of the dictionary in the parameters of the print() function, as shown in the following code. To access any of the values, you use square brackets with the name of the key enclosed in the brackets. If the key is a string, the key has to be enclosed in quotes. If the key is a number (you can use numbers, too, making a dictionary look a lot like a list or a tuple), you need only the bare number.

>>>print(menu_specials)

{‘lunch’: ‘split pea soup and garlic bread’, ‘breakfast’: ‘sausage and eggs’,

‘dinner’: ‘2 hot dogs and onion rings’}

>>> print("%s" % menu_specials["breakfast"])

sausage and eggs

>>> print("%s" % menu_specials["lunch"])

split pea soup and garlic bread

>>> print("%s" % menu_specials["dinner"])

2 hot dogs and onion rings

If a key that is a string is accidentally not enclosed in quotes when you try to use it within square brackets, Python will try to treat it as a name that should be dereferenced to find the key. In most cases, this will raise an exception — a NameError — unless it happens to find a name that is the same as the string, in which case you will probably get an IndexError from the dictionary instead!

Try It Out Getting the Keys from a Dictionary

Dictionaries can tell you what all of their keys are, or what all of their values are, if you know how to ask them. The keys method will ask the dictionary to return all of its keys to you as a view so that you can examine them for the key (or keys) you are looking for, and the values method will return all of the values as a view.

>>> hungry=menu_specials.keys()

>>>print(list(hungry))

lunch

breakfast

dinner

>>>starving=menu_specials.value()

>>>print(list(starving))

split pea soup and garlic bread

sausage and eggs

2 hot dogs and onion rings

How It Works

Both the keys and values methods return views, which you can assign and use like any normal view. When you have the items in a view from the keys method, you can use the items in the view, which are keys, to get their matching values from that dictionary. Note that while a particular key will lead you to a value, you cannot start with a value and reliably find the key associated with it. You try to find the key when you know only a value; you need to exhaustively test all the possible keys to find a matching value, and even then, two different keys can have the same values associated with them.

In addition, the way that dictionaries work is that each key is different (you can’t have two keys that are exactly the same), but you can have multiple duplicate values:

>>>menu={"breakfast" : "spam", "lunch" : "spam", "dinner": "Spam with a side of

Spam"}

>>>print(menu)

{‘lunch’: ‘spam’, ‘breakfast’: ‘spam’, ‘dinner’: ‘Spam with a side of Spam’}

>>> menu.get("lunch")

‘spam’

>>> menu.get("breakfast")

‘spam’

As you can see, Python has no problem allowing you to see multiple values in different keys. However, watch what happens when you try the following code, whose purpose is to try and create keys with the same name:

>>> menu2 = {"breakfast" : "spam", "breakfast" : "ham", "dinner": "Spam with a

side of Spam:"}

>>>menu2.get(“breakfast”)

‘ham’

What happened here? Although you did not get an error, there is still a mistake in your code. When you typed in the second key named “breakfast”, Python replaced the value in the first key with the same name, and replaced the value of the second key with the same name. 

Treating a String Like a List

Python offers an interesting feature of strings. Sometimes, it is useful to be able to treat a string as though it were a list of individual characters. It’s not uncommon to have extraneous characters at the end of a string. People may not recognize these, but computers will get hung up on them. It’s also common to only need to look at the first character of a string to know what you want to do with it. For instance, if you had a list of last names and first names, you could view the first letter of each by using the same syntax that you would for a list. This method of looking at strings is called slicing and is one of the fun things about Python:

>>> last_names = [ "Douglass", "Jefferson", "Williams", "Frank", "Thomas" ]

>>> print("%s" % last_names[0])

Douglass

>>> print("%s" % last_names[0][0])

D

>>> print("%s" % last_names[1])

Jefferson

>>> print("%s" % last_names[1][0])

J

>>> print("%s" % last_names[2])

Williams

>>> print("%s" % last_names[2][0])

W

>>> print("%s" % last_names[3])

Frank

>>> print("%s" % last_names[3][0])

F

>>> print("%s" % last_names[4])

Thomas

>>> print("%s" % last_names[4][0])

T

For example, you can use the letter positioning of strings to arrange them into groups in a dictionary based on the first letter of the last name. You don’t need to do anything complicated; you can just check to see which letter the string containing the name starts with and file it under that:

>>> by_letter = {}

>>> by_letter[last_names[0][0]] = last_names[0]

>>> by_letter[last_names[1][0]] = last_names[1]

>>> by_letter[last_names[2][0]] = last_names[2]

>>> by_letter[last_names[3][0]] = last_names[3]

>>> by_letter[last_names[4][0]] = last_names[4]

The by_letter dictionary will, thanks to string slicing, only contain the first letter from each of the last names. Therefore, by_letter is a dictionary indexed by the first letter of each last name. You could also make each key in by_letter reference a list instead and use the append method of that list to create a list of names beginning with that letter (if, of course, you wanted to have a dictionary that indexed a larger group of names, where each one did not begin with a different letter).

Remember that, like tuples, strings are immutable. When you are slicing strings, you are actually creating new strings that are copies of sections of the original string.

String Slicing is Very Useful

WARNING: If you’re new to programming, string slicing may seem like an unusual feature at first. Programmers who have used a lower-level language like C or C++ would have learned how to program viewing strings as special lists (and in Python you can also slice lists, as you’ll see later), so for them this is natural. For you, it will be a very convenient tool once you’ve learned how to control repetition over lists in Chapter 4. 

This article is excerpted from chapter 3 "Variables – Names for values" of the book "Beginning Python: Using Python 2.6 and Python 3.1" by  James Payne (ISBN: 978-0-470-41463-7, Wrox, 2010, Copyright Wiley Publishing Inc.)

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *