0

Everyone seems to claim that xrange returns a generator but it really is its own thing:

>>> xrange(10)
xrange(10)
>>> xrange(10).__bases__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'xrange' object has no attribute '__bases__'

What I don't understand is how xrange lets you check membership.

>>> a = xrange(10)
>>> 4 in a
True
>>> 4 in a
True

If this was a real generator, the second check would fail since we have already iterated through the 4.

>>> def mygen():
...     num = 0
...     while num < 100:
...             yield num
...             num += 1
...
>>>
>>> mygen()
<generator object mygen at 0x100618b40>
>>> list(mygen())
[0, ..., 99]
>>> x = mygen()
>>> 4 in x
True
>>> 4 in x
False

There seems to be something special about xrange that lets it do membership properly, as if it overrides __contains__.

wonton
  • 7,568
  • 9
  • 56
  • 93

1 Answers1

1

It turns out, xrange is indeed not a true <type 'generator'>.

At the cython level there is a generator that powers the range logic, but there is also a range_contains_long function which does the comparison, bypassing iteration of the generator.

Therefore, to call xrange a generator is slightly misleading.

wonton
  • 7,568
  • 9
  • 56
  • 93
  • 2
    Indeed, `xrange` is simply another type, like `generator`, that is an iterable (because it defines an `__iter__` method). – chepner Dec 06 '19 at 19:02
  • 1
    A generator is not a specific class, it's just a set of defined behavior. That doesn't mean the object can't have other behaviors above and beyond the generator requirements. Calling `xrange` a generator is definitely *not* misleading. – Mark Ransom Dec 06 '19 at 19:04
  • I suppose it depends on the definition of a generator but I see generator as `` which would not let you do this membership check. The origin of this question is that for the longest time I avoided `xrange` for membership checks under the incorrect assumption that it would fail like a generator type. – wonton Dec 06 '19 at 19:08