2

When I'm iterating over an iterable in python, I used to keep track of where I am in the iteration more or less like so, by creating an iterator outside the loop and incrementing it within:

i = 0
for x in res:
    if not(i % 10):
        print("On count {}".format(unicode(i)))
    f(x)
    i +=1

So, on my 6 lines of loop, 4 of them are dedicated to the counter i.

I know I can also use enumerate as:

for i,x in enumerate(res):
    if not(i % 10):
        print("On count {}".format(unicode(i)))
    f(x)

Which saves 2 lines.

I was wondering if there was /still/ a more concise way of dealing with this and providing pretty feedback to the user at some loop interval. I suspect there may be something built in to the __iter__ such that my x may be able to call some kind of count? x.___count___ or something such that I don't need to even call enumerate?

Also, does anyone have advice on providing this kind of feedback in a more functional style? How would I embed this counter in a map, rather than for-loop?

3 Answers 3

4

enumerate is definitely the correct way of doing this.

  1. Every python programmer knows enumerate. It's built-in and its intent is immediately obvious. Always go for built-in if possible.

  2. enumerate keeps your concerns separate. Walking a collection and counting iterations are different useful functions, reusable under different contexts.

  3. enumerate forces explicit declaration of the counter variable. If you're going to use it, this beats the hell out of __count__, a mysterious attribute spawned behind curtains. The sole fact of having enumerate tells the reader that the iteration index is important.

Go for enumerate.

Sign up to request clarification or add additional context in comments.

Comments

1

The only way I can think of to combine them, would be to write a generator, and have that do the printing. The overall code lines is the same, but you could write it once and use it multiple times:

def status_generator(iterable, update_interval=10):
    for i, x in enumerate(iterable):
        if not (i % update_interval):
            print("On count {}".format(unicode(i)))
        yield x

map(f, status_generator(my_iterable))

Comments

1

enumerate is the best and most widely used way to do what you've done. If you want to map functions onto iterables, you can always use a list comprehension or the map function, although a list comprehension is often more efficient in my experience. So if you want to change all the 10s into a string in a list:

>>> l = list(range(1, 100))
>>> ['a ten!' if i % 10 == 0 else v for i, v in enumerate(l)]
['a ten!', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'a ten!', 12, 13, 14, 15, 16, 17, 18, 19, 20, 'a ten!', 22, 23, 24, 25, 26, 27, 28, 29, 30, 'a ten!', 32, 33, 34, 35, 36, 37, 38, 39, 40, 'a ten!', 42, 43, 44, 45, 46, 47, 48, 49, 50, 'a ten!', 52, 53, 54, 55, 56, 57, 58, 59, 60, 'a ten!', 62, 63, 64, 65, 66, 67, 68, 69, 70, 'a ten!', 72, 73, 74, 75, 76, 77, 78, 79, 80, 'a ten!', 82, 83, 84, 85, 86, 87, 88, 89, 90, 'a ten!', 92, 93, 94, 95, 96, 97, 98, 99]

You can do the same with map:

>>> map(lambda x: 'a ten!' if x[0] % 10 == 0 else x[1], enumerate(l))
['a ten!', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'a ten!', 12, 13, 14, 15, 16, 17, 18, 19, 20, 'a ten!', 22, 23, 24, 25, 26, 27, 28, 29, 30, 'a ten!', 32, 33, 34, 35, 36, 37, 38, 39, 40, 'a ten!', 42, 43, 44, 45, 46, 47, 48, 49, 50, 'a ten!', 52, 53, 54, 55, 56, 57, 58, 59, 60, 'a ten!', 62, 63, 64, 65, 66, 67, 68, 69, 70, 'a ten!', 72, 73, 74, 75, 76, 77, 78, 79, 80, 'a ten!', 82, 83, 84, 85, 86, 87, 88, 89, 90, 'a ten!', 92, 93, 94, 95, 96, 97, 98, 99]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.