Understanding __getitem__ interaction with __len__
Straight from the docs:
If the
__reversed__()
method is not provided, thereversed()
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
Concatenating Two One-Dimensional Numpy Arrays
Asynchronous Method Call in Python
Building Python with Ssl Support in Non-Standard Location
Connecting Slots and Signals in Pyqt4 in a Loop
Matplotlib: Specify Format of Floats for Tick Labels
Using Beautifulsoup to Extract Text Without Tags
Calling the "Source" Command from Subprocess.Popen
Purpose of Calling Function Without Brackets Python
Style Active Navigation Element with a Flask/Jinja2 MACro
Color Coding Cells in a Table Based on the Cell Value Using Jinja Templates
Fast Way of Counting Non-Zero Bits in Positive Integer
Is It Ok to Use Dashes in Python Files When Trying to Import Them
Pick N Distinct Items at Random from Sequence of Unknown Length, in Only One Iteration
How to Map Numeric Data into Categories/Bins in Pandas Dataframe