Getting Started in Python
2.2 Using Objects: the list Class
2.2.2 Calling Methods
Having associated the identifiergrocerieswith an initially empty list, we wish to add some items. We do so by calling theappendmethod using the following syntax:
>>> groceries.append('bread')
>>>
We will see this general format often as we invoke methods upon our objects. This is similar to the notation introduced in Chapter 1, as in spot.sit( )or spot.fetch(slippers). Let’s take a closer look at the precise syntax of one of these statements. The information conveyed by this syntax includes several key components:
groceries
Starting at the left we must identify the particular object with which we want to interact.
In this example we use the identifiergroceries, which had been established earlier. Next comes a period (often pronounced “dot”). The dot indicates that we wish to access a member of the given object. Next is the name of a method, in this caseappend. Finally, we find a set of parentheses with any expected parameters included between them. Each piece of this syntax is significant.
The leftmost identifier is critical in starting such an interaction. Keep in mind that we may have many objects in our environment. For example if planning a party, we might use a listgrocerieswhen shopping for our supplies, and another listguestsof those people who are invited to the party. We could begin as
>>> groceries = list()
>>> guests = list()
In this case, two distinct instances of thelistclass are constructed, one labeled with the identifiergroceries and the other labeled with the identifierguests. If we want to add 'Chip'to our guest list, we must use the syntaxguests.append('Chip')as opposed to groceries.append('Chip').
The method name must be clearly identified because objects support many different behaviors. For example, we will soon see that lists support other methods such asremove andsort. In this example, we are specifically invoking theappendmethod.
Finally, the parentheses are used to formally invoke the method and to delimit param-eters that are being sent. In our example, the parameter 'bread' is intentionally enclosed in quotation marks although this is not always the case. The quotation marks denote this as a string of characters (more about strings is coming in Section 2.3). Had we not included quotes, the termbreadin the contextgroceries.append(bread)is presumed to be the name of anotheridentifier. The interpreter reports an error if no such identifier exists.
>>> groceries.append(bread)
Traceback (most recent call last): File "<stdin>", line 1, in -toplevel-NameError: name 'bread' is not defined
>>>
Don’t be too alarmed by the gibberish of the error message. As a beginning program-mer you will see many of them. Even experienced programprogram-mers make errors quite often when developing software. In fact, many of the details shown in Python’s error messages are meant to provide important diagnostic information to the programmer as to the cause of the error. The final line of the error message is usually the most informative, describing the type of error that occurred, in this case aNameError. Notice that the error does not cause the interpreter to quit. It simply ignores the troublesome command and presents us with a new prompt so that we can continue.
Another common error occurs when a method is called without the proper parame-ters. It is important that the number and type of parameters agree with the signature of the given method. In the case ofappend, the caller must send a single parameter, namely the object that is to be added to the list. It makes no sense to invokeappendwithout such a value and doing so results in an error message from the interpreter.
>>> groceries.append()
Traceback (most recent call last): File "<stdin>", line 1, in
-toplevel-TypeError: append() takes exactly one argument (0 given)
>>>
Going back to our lesson, theappendmethod is an example of a mutator. It changes the internal state of the list, adding the specified item to the end of the list. Of course, we do not typically see that internal state. However, we can take a peek by typing the identi-fier itself into the interpreter. This causes it to display a textual representation of our object.
>>> groceries ['bread']
In the case of lists, that representation uses square brackets with the contents of the list between those brackets. To see an example of a longer list, let’s add some more groceries.
>>> groceries.append('milk')
>>> groceries.append('cheese')
>>> groceries
['bread', 'milk', 'cheese']
We see the individual elements separated by commas when displayed. Notice that each element was appended to the end of the list. Thus 'milk' was placed after 'bread', and then 'cheese' after that. The same item can be added to the list in more than one position, as shown here.
>>> groceries.append('bread')
>>> groceries
['bread', 'milk', 'cheese', 'bread']
The insert method
The order of items on a grocery list may or may not be significant in real life. As our next example, we focus on a list where order is very significant: a restaurant’s waiting list.
Here, theappendmethod has natural semantics, adding a new person to the end of the list.
>>> waitlist = list()
>>> waitlist.append('Kim')
>>> waitlist.append('Eric')
>>> waitlist.append('Andrew')
>>> waitlist
['Kim', 'Eric', 'Andrew']
Perhaps by slipping some money to the maître d’, a new customer can improve his or her position on the list. The more money, the higher up the list! In Python, we simulate such a possibility by calling a method namedinsert, which allows us to add an item at a desired position. The signature of the method uses two parameters; the first specifies the position and the second the new item. We describe the desired position using a value known as an index. The index of the newly inserted item is the number of existing items that should remain in front of it. By this convention, inserting an item with index 0 places it at the very beginning of the list, with index 1 it is placed after the first item, and so on. As an example,
>>> waitlist.insert(1, 'Donald')
>>> waitlist
['Kim', 'Donald', 'Eric', 'Andrew']
>>> waitlist.insert(3, 'Grace')
>>> waitlist
['Kim', 'Donald', 'Eric', 'Grace', 'Andrew']
Notice that a new element does not replace any existing item; the trailing items are implic-itly pushed back one position in the list.
The index of an element equals the number of elements that precede it. Therefore, the first element of a list has index 0, the second has index 1, and so forth. For this reason, we say that lists are zero-indexed.
The remove method
What if somebody gets tired of waiting in line and decides to leave our restaurant? We can adjust our list accordingly by calling theremove method, specifying the item to be removed as a parameter.
>>> waitlist.remove('Eric')
>>> waitlist
['Kim', 'Donald', 'Grace', 'Andrew']
Earlier, we mentioned that a list may have several occurrences of the same value. So what happens if we use the syntaxwaitlist.remove('Alan')when our list has two or more people named 'Alan'? In this case, the semantics of the method are defined to remove the first such entry that it finds, searching from beginning to end. As an example, consider the following longer list:
>>> waitlist
['Rich', 'Elliot', 'Alan', 'Karl', 'Alan', 'William']
>>> waitlist.remove('Alan')
>>> waitlist
['Rich', 'Elliot', 'Karl', 'Alan', 'William']
Notice that the first person named 'Alan' was removed while the subsequent individual remains. Finally, we point out that if you try to remove an item that is not actually on the list, an error occurs.
>>> waitlist.remove('Stuart') Traceback (most recent call last):
File "<stdin>", line 1, in -toplevel-ValueError: list.remove(x): x not in list
This particular error is known as aValueError; we sent a parameter, as expected by the signature, but the value of that parameter was not legitimate for the intended action.