While we're talking about weird Python edge cases, what do you think this does?
```
class Foo:
def __getitem__(self, i):
return i
def __len__(self):
return 5
print(list(Foo()))
```
While we're talking about weird Python edge cases, what do you think this does?
```
class Foo:
def __getitem__(self, i):
return i
def __len__(self):
return 5
print(list(Foo()))
```
The answer is so counter-intuitive to me that I have independently discovered it twice, about a year apart, and posted outraged comments about it in discord both times, and the second time had to be reminded that this was not the first time I'd discovered the behaviour.
It's possible it's also not the first time I've posted it on mastodon.
@cfbolz Correct, yes. It will hang until it eventually runs out of memory.
Now, do you know why?
@DRMacIver @cfbolz Wild guess: the iteration doesn't consult `__len__`, but just keeps retrieving incremental indexes waiting for one to raise an exception.
UPDATE: my language is sloppy, but you get what I'm arm-waving about.
@cfbolz @tartley Yup, exactly that. Default iteration is like:
```
i = 0
while True:
try:
yield x[i]
except IndexError:
break
i += 1
```
Instead of:
```
for i in range(len(x)):
yield x[i]
```
In some sense you should never be able to tell the difference in correct code, so you'll rarely notice this, but I've at least twice managed to write incorrect code that doesn't raise for out of bounds access
This PEP proposes adding an nb_index slot in PyNumberMethods and an __index__ special method so that arbitrary objects can be used whenever integers are explicitly needed in Python, such as in slice syntax (from which the slot gets its name).