[pythonvis] Re: Slice of Py: Lists and Tuples

  • From: "Jim Snowbarger" <Snowman@xxxxxxxxxxxxxxxx>
  • To: <pythonvis@xxxxxxxxxxxxx>
  • Date: Mon, 22 Sep 2014 20:55:43 -0500

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.

Other related posts: