looping through a list

Peter Hansen peter at engcorp.com
Sat May 4 23:11:42 EDT 2002


[Reposting to possibly-still-relevant threads since all my outbound news 
has been swallowed for the last week.  Originally posted April 26.]

Hugh wrote:
> 
> I need help with a really simple algorithm that I can't get my head
> around. I am looping through the list returned by a dcoracle2 execute
> statement and need to add each item of data surrounded by some HTML
> table tags to a string called text which I then pass to a page
> generation method. At the moment I've got this:
> 
> result = c.fetchone()
>   if (result != ""):

In Python you can often test with just "if result:" rather than by
explicit comparison with the empty string.  Whether you really want
to do that depends on the context.  By the way, I hope your 
indentation wasn't as shown above, because that should generate
an IndentationError.  Always take care with spaces/tabs and
indentation when pasting Python code into the newsreader.

>     i=0
>     text = '<tr width = "80%" align = "center">'
>     while (i != len(result)+1):
>       i = i + 1

You're effectively writing code for some other language in the
guise of Python.  It can work, but there are generally much
simpler ways in Python than incrementing an index value as
if this were a "for" loop in another language.

Try this instead:

text = []
text.append('<tr ...>')
for char in result:
    text.append('<td ...>%s</td>' % char)
text.append('</tr>')
text = ''.join(text)

I've left out the if/else for simplicity, and replaced the attributes
with the "...".

A few note on the above (untested) code:

1. Concatenating strings can get very slow since you are 
actually creating a new string from the two old ones every
time you do that.  Strings in Python are "immutable", so you
cannot change them in place.  What looks like you are modifying
the string 'text' is actually creating a new string in a 
different location and *rebinding* the name 'text' to the
new string, and freeing up the memory for the old one.

2. As a result of item 1, the simplest way to avoid the potential
problems is to create a list, append all the string fragments you
want, then join them together at the end (as I did above, rebinding
the name 'text' from the list to the resulting, joined string.
You could use '\n'.join(text) if you wanted to join each fragment
with a newline in between, for example.

3. The "for" statement in Python will automatically iterate
through each element of a sequence like "result".  In the example
above, each element (a character) is assigned in sequence, one
at a time, to the name "char".  This is faster and much simpler
than using an index variable and manually indexing into "result"
as you were trying (and always avoids one-off errors such as your
index out-of-bounds problem).  As with other Python constructs,
this can easily avoid entire classes of bugs that commonly occur
with other languages.  Ain't Python the coolest? :)

4. The char can be inserted into the appropriate place in
the string either by joining multiple pieces together as you
did, or by using the "%" formatting operator.  Sometimes it's
six of one, a half dozen of the other, but I find it generally
more readable than a bunch of "+" concatenation, and it's
much more general and flexible.  In the case above, the "%s"
is replaced by the character in "char" in the resulting string.

5. As a rule, you're better off always posting code that you
have actually run, along with the exception that is generated.
This ensures we'll answer the right question...  in this case
it might not have mattered though.

> At the moment, it keeps saying list index out of bounds and I don't
> know where I'm going wrong. I've tried all sorts of things in the
> While statement and still can't get it going.

Of course, we could also debug the while statement, but why
bother... "for" is much easier.  :-)

Cheers,
-Peter



More information about the Python-list mailing list