# Counting iterations

Discussion in 'Python' started by Derek Basch, Apr 8, 2005.

1. ### Derek BaschGuest

Is there a better way to count iterations that this?:

pets = 0
for i in pets:
pets += 1
print "pet" + "#" + pets

Thanks,
Derek Basch

Derek Basch, Apr 8, 2005

2. ### Steven BethardGuest

Derek Basch wrote:
> Is there a better way to count iterations that this?:
>
> pets = 0
> for i in pets:
> pets += 1
> print "pet" + "#" + pets

for i, pet in enumerate(pets):
print 'pet#%i' % (i + 1)

STeVe

Steven Bethard, Apr 8, 2005

3. ### Will McGuganGuest

Derek Basch wrote:
> Is there a better way to count iterations that this?:
>
> pets = 0
> for i in pets:
> pets += 1
> print "pet" + "#" + pets
>

You can use 'enumerate' to get the index, but the code above wont work -
you are trying to iterate over a non-sequence.

Will McGugan

--
"".join( [ {'@':'@','.':'.'}.get(c,None) or chr(97+((ord(c)-97)+13)%26)
for c in "" ] )

Will McGugan, Apr 8, 2005
4. ### Derek BaschGuest

ooops you are right. Should have been:

pets = ["cat", "dog", "bird"]
num_pets = 0
for i in pets:
num_pets += 1
print "pet" + "#" + num_pets

That's the problem with one offs. I don't read them .

Derek Basch, Apr 8, 2005
5. ### Fredrik LundhGuest

Derek Basch wrote:

> ooops you are right. Should have been:
>
> pets = ["cat", "dog", "bird"]
> num_pets = 0
> for i in pets:
> num_pets += 1
> print "pet" + "#" + num_pets

Traceback (most recent call last):
File "example.py", line 5, in ?
print "pet" + "#" + num_pets
TypeError: cannot concatenate 'str' and 'int' objects

</F>

Fredrik Lundh, Apr 9, 2005
6. ### runesGuest

You should avoid the "a" + "b" + "c" -kind of concatenation. As strings
at immutable in Python you actually makes copies all the time and it's
slow!

The alternative used in Steven Bethard's example is preferable.

runes, Apr 9, 2005
7. ### Andrew DalkeGuest

runes wrote:
> You should avoid the "a" + "b" + "c" -kind of concatenation. As strings
> at immutable in Python you actually makes copies all the time and it's
> slow!

The OP wrote

print "pet" + "#" + num_pets

(properly str(num_pets) )

You recommended the "alternative used in Steven Bethard's example"

print 'pet#%i' % (i + 1)

because "it's slow". I disagree, it isn't for this code.
It's comparable in performance to interpolation and most
of the time is spent in converting int -> string. Indeed
if the object to be merged is a string then the addition
version is faster than interpolation.

Here's the details.

The string concatenation performance that you're
talking about doesn't hit until there are multiple
appends to the same string, where "multiple" is rather
more than 2. The advice usually applies to things like

text = ""
for line in open(filename, "U"):
text += line

which is much slower than, say
lines = []
for line in open(filename, "U")
lines.append(line)
text = "".join(lines)

or the more modern
text = "".join(open(filename, "U"))

to say nothing of

Anyway, to get back to the example at hand,
consider what happens in

"pet#%i" % (i+1)

(NOTE: most times that's written %d instead of %i)

The run-time needs to parse the format string
and construct a new string from the components.
Internally it does the same thing as

"pet#" + str(i+1)

except that it's done at the C level instead
Python and the implementation overallocates
100 bytes so there isn't an extra allocation
in cases like this.

Personally I would expect the "%" code to be
about the same performance as the "+" code.

Of course the real test is in the timing.
Here's what I tried. NOTE: I reformatted by
hand to make it more readable. Line breaks and
the \ continuation character may have introduced
bugs.

First, the original code along with the 'str()'
correction.

% python /usr/local/lib/python2.3/timeit.py -s \
'pets = ["cat", "dog", "bird"]' \
'num_pets=0' 'for pet in pets:' \
' num_pets += 1' \
' s="pet" + "#" + str(num_pets)'
100000 loops, best of 3:
14.5 usec per loop

There's no need for the "pet" + "#" so I'll
turn that into "pet#"

% python /usr/local/lib/python2.3/timeit.py -s \
'pets = ["cat", "dog", "bird"]' \
'num_pets=0' \
'for pet in pets:' \
' num_pets += 1' \
' s="pet#" + str(num_pets)'
100000 loops, best of 3: 12.8 usec per loop

That's 1.3 extra usecs.

By comparison here's the "%" version.

% python /usr/local/lib/python2.3/timeit.py -s \
'pets = ["cat", "dog", "bird"]'\
'num_pets=0' \
'for pet in pets:' \
' num_pets += 1' \
' s="pet#%s" % num_pets'
100000 loops, best of 3: 10.8 usec per loop

I'm surprised that it's that much faster - a
good 2 microseconds and that isn't the only
code in that loop.

But both the "%" and "+" solutions need to
convert the number into a string. If I
use an existing string I find

% python /usr/local/lib/python2.3/timeit.py -s \
'pets = ["cat", "dog", "bird"]' \
'num_pets=0' \
'for pet in pets:' \
' num_pets += 1' \
' s="pet#" + pet'
100000 loops, best of 3: 4.62 usec per loop

So really most of the time - about 8 usec - is
spent in converting int -> string and the
hit for string concatenation or interpolation
isn't as big a problem.

Compare with the string interpolation form
of the last version

% python /usr/local/lib/python2.3/timeit.py -s \
'pets = ["cat", "dog", "bird"]' \
'num_pets=0' 'for pet in pets:' \
' num_pets += 1' \
' s="pet#%s" % pet' \
100000 loops, best of 3: 7.55 usec per loop

In this case you can see that the % version is
slower (by 2usec) than the + version.

I therefore disagree with the idea that simple
string concatenation is always to be eschewed
over string interpolation.

Andrew

Andrew Dalke, Apr 9, 2005
8. ### Steven BethardGuest

Andrew Dalke wrote:
> "pet#%i" % (i+1)
>
> (NOTE: most times that's written %d instead of %i)

Any reason to prefer %d over %i? The library reference seems to suggest
that they're the same thing[1]. I've typically used %i since I can
remember it from the int type, like I can remember %f from the float
type. Is there any reason not to?

STeVe

[1] http://docs.python.org/lib/typesseq-strings.html

Steven Bethard, Apr 10, 2005
9. ### runesGuest

runes, Apr 10, 2005
10. ### Derek BaschGuest

Interesting stuff Andrew. I do generally avoid string concantination
for the reason listed in the Python Performance Tips but your analysis
kinda puts that in question. Such a dense discussion for me just trying
to find the enumerate() function . I guess that is why the python
list is so great. You guys always rip my code apart and correct my
style. Even if it is just a stupid one off example.

Thanks everyone,
Derek Basch

Derek Basch, Apr 10, 2005
11. ### Terry ReedyGuest

"runes" <> wrote in message
news:...
>
> [Andrew Dalke]
>> I therefore disagree with the idea that simple
>> string concatenation is always to be eschewed
>> over string interpolation.

String cat (+) is fine for joining a few short strings.

> Andrew, what you write makes sense. I've never really tested it, just
> read it several places, fx here:
> http://www.python.org/moin/PythonSpeed/PerformanceTips#stringcat

This is about making long strings in situations where performance is an
issue.

Terry J. Reedy

Terry Reedy, Apr 11, 2005
12. ### Andrew DalkeGuest

Derek Basch wrote:
> Interesting stuff Andrew. I do generally avoid string concantination
> for the reason listed in the Python Performance Tips but your analysis
> kinda puts that in question.

Thanks.

It was interesting for me to. I hadn't looked at the implementation
for string % before and was rather surprised to find that it
overallocates 100 bytes to reduce the number of allocs done. It's
simpler than the implementation I considered.

Andrew

Andrew Dalke, Apr 11, 2005