Understanding _Getitem_ Method

Understanding __getitem__ interaction with __len__

Straight from the docs:

If the __reversed__() method is not provided, the reversed()
built-in will fall back to using the sequence protocol (__len__()
and __getitem__()).

__getitem__ method receives int instead of slice obj

As per the docs:

For sequence types, the accepted keys should be integers and slice objects

You can check if the key is a slice or not and create one:

class MyClass2:
def __getitem__(self, key):

a_list = [1,2,3,4]
keys_to_iterate = key if isinstance(key, slice) else slice(key, key+1)
print(key)

for val in a_list[keys_to_iterate]:
# do stuff
pass

print('it worked')

Running example: https://repl.it/repls/SmallDarkblueWifi

How do dunder methods __getitem__ and __len__ provide iteration?

A iterable is a class which defines either __iter__ or __getitem__, no need for __len__.

The difference between the __iter__ implementation and the __getitem__ implementataion is:
__iter__ calls __next__ on the object that returned from __iter__ (aka iterator), until it reaches StopIteration and that's where the for loop stops.
However __getitem__, starts from zero (always), and each iteration it increments by one, until it reaches IndexError, and it does that by obj[idx].

For instance:

class GetItem:
def __getitem__(self, idx):
if idx == 10:
raise IndexError
return idx

for i in GetItem():
print(i)

The result will be

0
1
2
...
9

because as soon as the index gets to 10, it raises IndexError and the loop stops.

__iter__ on the other hand,

class Iter:
def __iter__(self):
self.n = 0
return self

def __next__(self):
self.n += 1
if self.n == 10:
raise StopIteration
return self.n

for i in Iter():
print(i)

Here, you need to keep track of the state by yourself, whereas in __getitem__ it does it by itself, it's better for counting/indexing and such.

Assigning (instead of defining) a __getitem__ magic method breaks indexing

Special methods (essentially anything with two underscores on each end) have to be defined on the class. The internal lookup procedure for special methods completely skips the instance dict. Among other things, this is so if you do

class Foo(object):
def __repr__(self):
return 'Foo()'

the __repr__ method you defined is only used for instances of Foo, and not for repr(Foo).

How do I use __getitem__ and __iter__ and return values from a dictionary?

def __iter__(self): return self.books.itervalues()

Implementing __getitem__ in new-style classes

This is documented in the Python datamodel documentation: Special method lookup for new-style classes:

For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

and

The rationale behind this behaviour lies with a number of special methods such as __hash__() and __repr__() that are implemented by all objects, including type objects. If the implicit lookup of these methods used the conventional lookup process, they would fail when invoked on the type object itself[.]

So, because both hash(int) and hash(1) must work, special methods are looked up on the type instead of on the instance. If __hash__() was looked up straight on the object, hash(int) would be translated to int.__hash__(), and that would fail, because int.__hash__() is an unbound method and it expects to be called on an actual instance of int() (e.g. 1); so for hash(int), type.__hash__() should called instead:

>>> hash(1) == int.__hash__(1)
True
>>> hash(int) == type.__hash__(int)
True
>>> int.__hash__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor '__hash__' of 'int' object needs an argument

This is a backwards-incompatible change, so it only applies to new-style objects.



Related Topics



Leave a reply



Submit