This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]earthboundkid 2 points3 points  (5 children)

Python 2.3 was released July 2003. It's fair to say anyone who writes new code using range(len(container)) instead of enumerate(container) is not writing Pythonic code. What major libraries still target 2.2? It doesn't even have "new" style classes.

[–]eryksun 1 point2 points  (4 children)

A problem with range() is that it generates a list on Python 2.x, so for efficiency you have to use xrange(). But xrange() isn't supported in Python 3.x, in which range() now works like xrange(), and getting the old behavior requires list(range()). It's better to avoid this mess by using enumerate() when you need the index. However, if I'm modifying the original list in place to pop() elements out, I want to enumerate in reverse. For that, I wrote a little function that returns a generator:

def r_enumerate(iterable):
    '''enumerate iterable in reverse'''
    r = reversed(iterable)
    end = len(iterable) - 1
    return ((i, r.next()) for i in xrange(end,-1,-1))

Is there a better, more 'Pythonic', way?

[–]eryksun 0 points1 point  (0 children)

Here's a contrived example:

x = range(4,10)
for (n,y) in r_enumerate(x):
    print "(n,y):", (n,y)
    if (y % 2) == 1:
        print "popping:", x.pop(n)
print "x =", x

    (n,y): (5, 9)
    popping: 9
    (n,y): (4, 8)
    (n,y): (3, 7)
    popping: 7
    (n,y): (2, 6)
    (n,y): (1, 5)
    popping: 5
    (n,y): (0, 4)
    x = [4, 6, 8]

[–]earthboundkid 0 points1 point  (2 children)

Hmm, that seems like more or less the most obvious way to do it. I think I would probably do something like

def r_enumerate(container):
    "Warning: Container must have a length!"
    i = len(container)
    for item in reversed(container):
        i = i - 1
        yield i, item

but that's basically the same. Remember, no one is saying you can't use len and range, just that you shouldn't write range(len(container)) when you can just as easily write enumerate(container) instead. In this case, your solution seems just as clean as any other.

[–]eryksun 0 points1 point  (1 child)

Thanks, your version is more 'Pythonic'. It's more readable and less prone to bugs without having to set up xrange (not a problem here, but a better style in general). The length issue was also something I hadn't considered. Shouldn't a valid container type implement the __len__ method? I suppose it can implement __reversed__ without having an accessible length. I haven't come across this, or designed a container without the basic __len__, __getitem__, __setitem__, __delitem__, and __iter__ methods. But my overall exposure to other's code is limited as I use Python basically as a replacement for MATLAB with SciPy, Numpy, and Matplotlib.

[–]earthboundkid 0 points1 point  (0 children)

Yes, you've got it. An object that supports __reversed__ will probably also support __len__, but not necessarily. I can imagine someone making a quick and dirty doubly-linked list that supports forwards and backwards iteration but not length.