This becomes important because Python, by default, copies only a reference to a variable rather than the full variable. For example:
>>> list1 = [1, 2, 3]
>>> list2 = [4, list1, 6]
>>> list1
[1, 2, 3]
>>> list2
[4, [1, 2, 3], 6]
Here you can see a nested list. list2 contains 4, and then list1, and then 6. When you print the value of list2, you can see it also contains list1. Now, proceeding on from that:
>>> list1[1] = "Flake"
>>> list2
[4, [1, 'Flake', 3], 6]
Line one sets the second element in list1 (remember, sequences are zero-based!) to be Flake rather than 2; then the contents of list2 are printed. As you can see, when list1 changed, list2 was updated also. The reason for this is that list2 stores a reference to list1 as opposed to a copy of list1; they share the same value.
You can show that this works both ways by indexing twice into list2, like this:
>>> list2[1][1] = "Caramello"
>>> list1
[1, 'Caramello', 3]
The first line says, "get the second element in list2 (list1) and the second element of that list, and set it to be 'Caramello'." Then list1's value is printed, and you can see it has changed. This is the essence of mutability: We are changing our list without creating a new list. On the other hand, editing a string creates a new string, leaving the old one unaltered. For example:
>>> mystring = "hello"
>>> list3 = [1, mystring, 3]
>>> list3
[1, 'hello', 3]
>>> mystring = "world"
>>> list3
[1, 'hello', 3]
Of course, this raises the question of how you copy without references when references are the default. The answer, for lists, is that you use the [:] slice, which you saw earlier. This slices from the first element to the last, inclusive, essentially copying it without refer ences. Here is how that looks:
>>> list4 = ["a", "b", "c"]
>>> list5 = list4[:]
>>> list4 = list4 + ["d"]
>>> list5
['a', 'b', 'c']
>>> list4
['a', 'b', 'c', 'd']
Lists have their own collections of built-in methods, such as sort(), append(), and pop(). The latter two add and remove single elements from the end of the list, with pop() also returning the removed element. For example:
>>> list5 = ["nick", "paul", "julian", "graham"]
>>> list5.sort()
>>> list5
['graham', 'julian', 'nick', 'paul']
>>> list5.pop() 'paul'
>>> list5
['graham', 'julian', 'nick']
>>> list5.append("Rebecca")
In addition, one interesting method of strings returns a list: split(). This takes a character by which to split and then gives you a list in which each element is a chunk from the string. For example:
>>> string = "This is a test string";
>>> string.split(" ")
['This', 'is', 'a', 'test', 'string']
Lists are used extensively in Python, although this is slowly changing as the language matures.
Dictionaries
Unlike lists, dictionaries are collections with no fixed order. Instead, they have a key (the name of the element) and a value (the content of the element), and Python places them wherever it needs to for maximum performance. When defining dictionaries, you need to use braces ({ }) and colons (:). You start with an opening brace and then give each element a key and a value, separated by a colon, like this:
>>> mydict = { "perl" : "a language", "php" : "another language" }
>>> mydict
{'php': 'another language', 'perl': 'a language'}
This example has two elements, with keys perl and php. However, when the dictionary is printed, we find that php comes before perl — Python hasn't respected the order in which they were entered. You can index into a dictionary using the normal code:
>>> mydict["perl"] 'a language'
However, because a dictionary has no fixed sequence, you cannot take a slice, or index by position.
Like lists, dictionaries are mutable and can also be nested; however, unlike lists, you cannot merge two dictionaries by using +. A key is used to locate dictionary elements, so having two elements with the same key would cause a clash. Instead, you should use the update() method, which merges two arrays by overwriting clashing keys.
You can also use the keys() method to return a list of all the keys in a dictionary.
Conditionals and Looping
So far, we have been looking at just data types, which should show you how powerful Python's data types are. However, you simply cannot write complex programs without conditional statements and loops.
Python has most of the standard conditional checks, such as > (greater than), <= (less than or equal to), and == (equal), but it also adds some new ones, such as in. For example, you can use in to check whether a string or a list contains a given character/element:
>>> mystring = "J Random Hacker"
>>> "r" in mystring
True
>>> "Hacker" in mystring
True
>>> "hacker" in mystring
False
The last example demonstrates how in is case sensitive. You can use the operator for lists, too:
>>> mylist = ["soldier", "sailor", "tinker", "spy"]
>>> "tailor" in mylist
False
Other comparisons on these complex data types are done item by item:
>>> list1 = ["alpha", "beta", "gamma"]
>>> list2 = ["alpha", "beta", "delta"]
>>> list1 > list2
True
list1's first element (alpha) is compared against list2's first element (alpha) and, because they are equal, the next element is checked. That is equal also, so the third element is checked, which is different. The g in gamma comes after the d in delta in the alphabet, so gamma is considered greater than delta and list1 is considered greater than list2.