Dumb Q #1

Alex Martelli aleax at aleax.it
Wed Jan 29 05:56:57 EST 2003


Norm wrote:

> These replies were great!  I've been reading python books every night, but
> having live thoughts and explanations really help.
> 
> Could you / would you expound on what you mean in #1 of your answer?
> iterate a slice of it, ie:   for record in records[:]:       I'm having
> trouble visualizing it.

It's not really relevant to your original question (for which the
simplest, soundest approach is to use "for i in range(len(records)):"
instead).  But suppose you had the following problem, e.g.:

"records is a list of strings.  Append to that list a copy of each
string in it that ends with a colon, shorn of its first character."

The intuitive approach might be...:

>>> records='uno due: tre qua:'.split()
>>> for r in records:
...   if r.endswith(':'):
...     records.append(r[1:])
...
>>> records
['uno', 'due:', 'tre', 'qua:', 'ue:', 'ua:', 'e:', 'a:', ':', ':', '', '']
>>>

As you see it doesn't work as intended -- BECAUSE the loop is
modifying the very list on which it's iterating.  In this case
the loop "sees" the items appended by earlier legs of the same
loop and ends up working on them too; in other cases it could
be even worse, e.g. if you didn't have the "shorn of its first
character" part you'd end up looping forever.

The loop needs to modify a list DISTINCT from the one it's looping
on.  Since the specs say that you must modify 'records', it's
simplest and most natural to loop on a list distinct from
'records' -- a COPY of list 'records' -- so that further changes
to list 'records' will not change the copy.

Simplest way to get "a copy of list 'records'" in Python is by
the expression:
    records[:]
which is a "whole-list slice", common idiom for "a copy".  So:

>>> records='uno due: tre qua:'.split()
>>> for r in records[:]:
...   if r.endswith(':'):
...     records.append(r[1:])
...
>>> records
['uno', 'due:', 'tre', 'qua:', 'ue:', 'ua:']
>>>

...exactly as desired.  Alternatively,

>>> import copy
>>> for r in copy.copy(records):
(rest same as above again)

might be considered a clearer, more readable, more explicit
way to do the same job -- module copy has the SPECIFIC task
of letting you ask for a copy of any kind of object.  However,
the "whole-list slice" idiom is SO common (even though not
QUITE as readable) that you'd better acquaint yourself with
it, to ease reading of other people's code, even if you (quite
sensibly IMHO) choose to standardize your OWN coding on the
use of module copy to ask for copies.


Alex






More information about the Python-list mailing list