With the following code :
import types
class Foo():
def __getitem__(self, x):
return x
def new_get(self, x):
return x + 1
x = Foo()
x.__getitem__ = types.MethodType(new_get, x)
x.__getitem__(42)
will return 43, but x[42]
will return 42.
Is there a way to override __getitem__
at instance level in Python?
__getitem__(x, i) . The method __getitem__(self, key) defines behavior for when an item is accessed, using the notation self[key] . This is also part of both the mutable and immutable container protocols. Unlike some other languages, Python basically lets you pass any object into the indexer.
In Python method overriding occurs by simply defining in the child class a method with the same name of a method in the parent class. When you define a method in the object you make this latter able to satisfy that method call, so the implementations of its ancestors do not come in play.
This is unfortunately, and quite surprisingly, not allowed:
For custom 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.
Source: https://docs.python.org/3/reference/datamodel.html#special-lookup
The item lookup protocol will always recover __getitem__
from the class, it will not even look at instance __dict__
. This is actually a good thing in general as doing otherwise would allow instances of the same class to be conceptually different from one another, which goes against the whole idea behind classes.
Nonetheless, there are situation where this could potentially be helpful, by example when monkey-patching for test purpose.
Because the dunder is looked up directly at class level, the item lookup logic must also be updated at the class level.
A solution is thus to update __getitem__
so that it first looks for an instance-level function in the instance __dict__
.
Here is an example where we are subclassing dict
to allow for instance-level __getitem__
.
class Foo(dict):
def __getitem__(self, item):
if "instance_getitem" in self.__dict__:
return self.instance_getitem(self, item)
else:
return super().__getitem__(item)
foo = Foo()
foo.instance_getitem = lambda self, item: item + 1
print(foo[1]) # 2
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With