Another terrific summary, my friend. Thanks very much for it. But, I may have misplaced my fork a point of confusion: t3 = ((1, 'b'), ['a', 2]) # nested tuple or list I thought this was a 2-dimentional tuple. but when I say: t3[1] it says that index is out of bounds. Yet, t3[0] only yields (1, "a") why can I not see the second item? I had the impression that, while tupels themselves are immutable, the objects they contain may not be? So, I thought I could get a handle to the list by saying: l1 = t3[1] and then operate on l1 like it was any list. But, I don't seem to understan this. Where have I jumped the track? l = t3[1] ----- Original Message ----- From: Richard Dinger To: pythonvis Sent: Saturday, September 20, 2014 1:32 PM Subject: [pythonvis] Slice of Py: Lists and Tuples At this point your Python scripts probably are beginning to require some kind of software containers for storing and fetching the data that the script is processing. These containers are known as data structures. This Slice will provide a description of the list and its close relative the tuple. The list and tuple are both core Python data structures. A data structure is a particular way of organizing data in the computer so it can be used effectively and efficiently. The list and tuple are both sequence objects and so all the sequence operations apply to these two data structures. The List Lists are the most common data structure used in Python scripts. The list is the real workhorse data structure. Many of the core methods in Python take arguments that are lists and return results in lists The Properties of Lists 1 Lists are containers of objects. 2 Lists are ordered sequences of objects 3 Member objects can be of arbitrary type 4 Member objects are accessed by offset. 5 Lists are variable length container 6 Lists are mutable The Python list is, in an informal sense, just a list of stuff. The list is an ordered sequence of objects of arbitrary type. Since the list is mutable , we finally have something that can be changed. And lists can be arbitrarily nested. List Literals The literal for a list is a comma separated sequence of objects enclosed in square brackets: >>> l1 = [1, 'b', 3] # mixed types >>> l2 = [] # an empty list >>> l3 = [[1, 2], [3, 4]] # nested lists List Operations As a sequence, the len, min, max and so on built-in functions all apply. You can index a member of the list and you can either fetch from or assign to a slice of the list. >>> l1 = list('abc') # makes list of iterable values >>> l1 # show values ['a', 'b', 'c' >>> l1[1] = 'x' # assign value >>> l1[:2] # a slice ['a', 'x'] List Methods The list methods are: 1 append(item) appends item to end of list >>> l0 = ['a', 'b', 'c'] >>> l0.append('9') l0 is now: ['a', 'b', 'c', '9'] 2 insert(index, item) inserts item before index >>> l0.insert(0, 'first') l0 is now: ['first', 'a', 'b', 'c', '9'] 3 pop() removes and returns last item >>> l0.pop() 9 l0 is now: ['first', 'a', 'b', 'c'] 4 pop(index) remove and return item at index >>> l0.pop(0) 'first' l0 is now: ['a', 'b', 'c'] 5 remove(value) remove first occurrence of value >>> l0.remove('b') l0 is now: ['a', 'c'] 6 reverse() reverse the list in place >>> l0.reverse() l0 is now: ['c', 'a'] 7 sort() sort the list in place this method is very fast >>> l0.sort() l0 is now: ['a', 'c'] 8 count(value) return number of occurrences of value >>> l0.count('c') 1 9 index(value) return index of first occurrence of value >>> l0.index('c') 1 10 extend(iterable) append each item in iterable >>> l0.extend('123') l0 is now: ['a', 'c', '1', '2', '3'] run dir(list) or dir ([]) to see all the list methods and use help to clarify arguments. Caution, since a list is mutable and function arguments are passed by assignment, a list changed in a function is changed everywhere. This catches most of us sooner or later. More on the sort method The list sort method is very flexible and powerful. You have some options in the sort method. The call syntax is: sort(cmp=None, key=None, reverse=False where: cmp is a compare function that takes two list items as arguments. If arg1 is less return -1, if arg1 is greater return +1, if args are equal return 0. key is a function that can be called with any list item as the argument and the sort method will compare key(arg1) and key(arg2) instead of comparing arg1 and arg2. reverse reverses the sense of the comparison and is not the same as reversing after sorting. For example say you have a list of strings: >>> words = ['The', 'quick', 'red', 'fox'] And you want to sort them by length >>> words.sort(key=len) >>> words ['The', 'red', 'fox', 'quick'] List Comprehensions You may recall that lists can be constructed with what is called a list comprehension. This is sort of a shorthand for using a for loop to inspect each item in an iterable and build a new list by appending an expression computed on the members of the iterable. A typical loop: result = [] # make empty result list for target in iterable: # loop through iterable result.append(expression) # expression involving target Note an if condition or another nested loop may appear between the first for loop and the append method call. The expression known as a list comprehension codes this idiom directly in a compact form. The list comprehension expression can be used where ever an expression can be used. The syntax of a list comprehension is: [ expression for target in iterable lc-clauses ] Target and iterable are the same as a regular for statement, but no colon. The lc-clauses are zero or more clauses using one of the two forms: for target in iterable if expression Here are some examples, first one loop: [c*2 for c in 'abc'] makes ['aa', 'bb', 'cc'] A loop and an if clause: [x for x in range() if x%2 == 0] makes [2, 4, 6, 8] Two loops: [x+y for x in 'abc' for y in '123'] makes ['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3'] A list comprehension nested inside another list comprehension: [[x+y for x in 'abc'] for y in '123'] makes [['a1', 'b1', 'c1'], ['a2', 'b2', 'c2'], ['a3', 'b3', 'c3]] Avoiding Reference Sharing Once someone knows that multiplying a list by n repeats the list n times, they may try using it to initialize a multidimensional list. For example: >>> l = [0] *4 >>> print l [0, 0, 0, 0] That works fine for a one dimensional list. But say the problem grows to two dimensions: >>> l = [[0] * 4] * 3 >>> print l [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] While this may look like just what you want, there is a subtle error in this list. Try rebinding l[0][0] to see what you get: >>> l[0][0] = 99 >>> print l [[99, 0, 0, 0], [99, 0, 0, 0], [99, 0, 0, 0]] That is probably not what you wanted! So what happened? Look at this in layers, first we have: [0] * 4 That produces a list with four references to zero, so far so good. Then we have: (our list of four zeros) * 3 And that produces a list of three references to the first list, which is not what we want. The correct way to initialize a two dimensional list is to use a list comprehension: >>> l = [[0 for col in range(4)] for row in range(3)] Using The Enumerate Built-in The preferred way to loop through a sequence or other iterable object is to simply iterate on the sequence: for item in sequence: process item This is simpler and faster than using indexing: for i in range(len(sequence)): process sequence[i] But sometimes you need the index in order to rebind a member of the sequence. Here is where the built-in enumerate function can help. The enumerate takes any iterable (not just sequences) and returns an iterable that returns all the (index, item) pair values for the original iterable. Enumerate( ['a', 'b', 'c']) for example will return (0, 'a'), (1, 'b'), (2, 'c'). Therefore, to process that sequence where some rebinding was required: for i, item in enumerate(sequence): if needs Changing(item): sequence[i] = revise(item) Enumerate is more useful than you may think at first glance. But keep it in mind as eventually you will need it. That wraps up our look at lists so now on to tuples. The Tuple The Python tuple is, in a sense, just an immutable list. Like a list, the tuple is an ordered sequence of objects of arbitrary type. Tuples can be arbitrarily nested. The literal for a tuple is a comma separated sequence of objects enclosed in parentheses: >>> t1 = (1, 'b', 3) # mixed types >>> t2 = (42,) # single value, note the comma >>> t3 = ((1, 'b'), ['a', 2]) # nested tuple or list >>> t3[1] # second item in t3 ['a', 2] >>> t3[1][0] # first item in second item 'a' As a sequence, the len, min, max and so on built-in functions all apply. You can index a member of the list and you can fetch a slice of the list. But since the tuple is immutable you can't assign to either a specific index or a slice. The tuple only supports two methods: 1 count(value) return number of occurrences of value 2 index(value) return index of first occurrence of value So why do we have a type that is like a list, but supports fewer methods? The immutability is the whole point. You can pass tuples around all you want without corrupting them. A list, however, can be changed by any routine the list is passed to.