If looping over a list/tuple/sequence, you can use len(...)
to infer how many times the loop was executed. But when looping over an iterator, you cannot.
[Update for clarity: I am thinking about single-use finite iterators where I want to do computation on the items AND count them at the same time.]
I currently use an explicit counter variable as in the following example:
def some_function(some_argument):
pass
some_iterator = iter("Hello world")
count = 0
for value in some_iterator:
some_function(value)
count += 1
print("Looped %i times" % count)
Given there are 11 characters in "Hello world"
,
the expected output here is:
Looped 11 times
I have also considered this shorter alternative using enumerate(...)
but I do not find this as clear:
def some_function(some_argument):
pass
some_iterator = iter("Hello world")
count = 0 # Added for special case, see note below
for count, value in enumerate(some_iterator, start=1):
some_function(value)
print("Looped %i times" % count)
[Update for reference: @mata spotted that as originally written this second example would fail if the iterator is empty. Inserting count = 0
solves this, or we can use the for ... else ...
structure to handle this corner case.]
It does not use the index from enumerate(...)
within the loop, but rather setting the variable to the loop count is almost a side effect. To me this is quite unclear, so I prefer the first version with the explicit increment.
Is there an accepted Pythonic way to do this (ideally for both Python 3 and Python 2 code)?
Use the enumerate() function to count in a for loop, e.g. for index, item in enumerate(my_list): . The function takes an iterable and returns an object containing tuples, where the first element is the index, and the second - the item. Copied!
for loops are used when you have a block of code which you want to repeat a fixed number of times. The for-loop is always used in combination with an iterable object, like a list or a range. The Python for statement iterates over the members of a sequence in order, executing the block each time.
You can combine the convenience of enumerate
with the counter being defined if the loop did not run by adding one line:
count = 0 # Counter is set in any case.
for count, item in enumerate(data, start=1):
doSomethingTo(item)
print "Did it %d times" % count
If all you need it is to count the number of items in an iterator, without doing anything with the items, and without making a list of them, you can do it simply:
count = sum(1 for ignored_item in data) # count a 1 for each item
You can do all sorts of stuff to count the number of items in a generator, but in any case, the original generator will be wasted. Exhausted, to be precise.
length = sum(1 for x in gen)
length = max(c for c, _ in enumerate(gen, 1))
length = len(list(gen))
gen
is too big, but it's just easy to understand, so that nobody will have to think hard banging their head against a wall trying to understand what this means. All of these will work only for finite generators.
If you want to calculate the 'length' of the iterator while looping over it, you can do this:
length = 0
for length, data in enumerate(gen, 1):
# do stuff
Now, length
will be equal to the number of elements the generator has produced. Notice that you don't have to increment length
manually as both length
and data
are still available and valid after the loop execution.
EDIT: if you want to execute some function for each value and disregard its return value (you can handle it by using a list as one of the function's arguments), you can try this:
length = sum(1 | bool(function(x)) for x in gen)
This will calculate the length while applying function
to each element of the generator. Still, using enumerate
looks like a better idea.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With