Python iterators considered harmful

I just tracked down a nasty bug in my code to a gotcha with Python iterators.

Consider the following code...


class Numbers(list):
    def even(self):
        for val in self:
            if val % 2 == 0:
                yield val
    even = property(even)

    def odd(self):
        for val in self:
            if val % 2 != 0:
                yield val
    odd = property(odd)

nums = Numbers(range(10))

# [0, 2, 4, 6, 8]
print `list(nums.even)`

# [0, 2, 4, 6, 8]
print `list(nums.even)`

even = nums.even

# [0, 2, 4, 6, 8]
print `list(even`)

# GOTCHA!
# []
print `list(even`)

This is evil. Watch out for it. In this example, if you don't actually need iterators you could just as easily rewrite Numbers using list comprehension:


class Numbers(list):
    def even(self):
        return [ val for val in self if val % 2 == 0 ]
    even = property(even)

    def odd(self):
        return [ val for val in self if val % 2 != 0 ]
    odd = property(odd)

No subtle gotchas and the code is even shorter!

Add new comment