[C++-sig] Re: Adding __len__ to range objects

David Abrahams dave at boost-consulting.com
Mon Aug 11 15:15:20 CEST 2003


Raoul Gough <RaoulGough at yahoo.co.uk> writes:

> David Abrahams <dave at boost-consulting.com> writes:
>
>> Raoul Gough <RaoulGough at yahoo.co.uk> writes:
> [snip]
>>> in template <...> struct iterator_range, add
>>>
>>>   typename std::iterator_traits<Iterator>::difference_type
>>>   distance () const {
>>>     return std::distance (m_start, m_finish);
>>>   }
> [snip]
>>
>> It only works for random-access iterators.  You could fix it so that
>> it would only create the __len__ function for random-access
>> iterators; that would be better.
>
> Yes, that would be easy enough I guess. BTW, do you mean it wouldn't
> work at all for (e.g.) forward_iterators, or just that it isn't as
> efficient? I thought std::distance worked for most (or all)
> categories, but is only constant time for random_access_iterators.
> It's still going to be (a lot) faster than the equivalent code in
> Python.

You're right, it would work for everything input iterator and above,
which is everything that range() works on.

>> Then, also, range() creates an iterator-returning function, not an
>> iteratable-returning function.  There are subtle differences.
>>
>>     for x in some_list:
>>        print x
>>
>>     for x in some_list:
>>        print x  # prints the same elements
>>
>> but:
>>
>>     for x in some_iterator:
>>        print x
>>
>>     for x in some_iterator:
>>        print x  # prints nothing
>
> Yes, I would actually expect this since I know that it is an iterator
> pair :-) 

I think you're slightly confused.  It is that in C++, but in Python it
is an iterator.  Traditionally, len(x) only applies to iterables in
Python.

> On the other hand, the following should work OK, unless I'm
> mistaken:
>
> iterator_copy = some_iterator
> for x in some_iterator: print x
> for x in iterator_copy: print x    # Prints same

You're mistaken.  iterator_copy and some_iterator both refer to the
same object.  Try it:

    >>> i = iter(range(5))
    >>> j = i
    >>> for x in i: print x
    ...
    0
    1
    2
    3
    4
    >>> for x in j: print x
    ...

>> None of the Python iterators support __len__ and I wonder if it's a
>> good idea to add it to an iterator we create.  Your thoughts?
>
> Yes, maybe it isn't such a great idea after all. One real drawback
> that I thought of is that adding this might break existing code. A
> (broken) user-defined iterator might not provide enough smarts for
> std::distance to work, and yet still be compatible with the existing
> range support. 

It would always supply enough smarts.  However, I am nervous about
supplying __len__ for anything below random-access, especially for
input iterators where it is probably destructive.

> Adding len would break this, since the distance function would get
> generated even if it is never used or wanted.

I don't think that's a problem.

> I can think of two ways around this: add a new range_with_len type
> (seems excessive), or provide some way for the client code to access
> the generated class_ object to add their own extensions (probably
> difficult?)

I think you're barking up the wrong tree.  Maybe we ought to simply
change our tune and say that range() produces an iterable-returning
function rather than an iterator-returning function.  If we did that
we could always generate __len__, and for that matter we could also
generate __getitem__/__setitem__ for random-access ranges.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com





More information about the Cplusplus-sig mailing list