How to Override the [] Operator in Python

How to override the [] operator in Python?

You need to use the __getitem__ method.

class MyClass:
def __getitem__(self, key):
return key * 2

myobj = MyClass()
myobj[3] #Output: 6

And if you're going to be setting values you'll need to implement the __setitem__ method too, otherwise this will happen:

>>> myobj[5] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__setitem__'

Override Python's 'in' operator?

MyClass.__contains__(self, item)

Can I overload operators for builtin classes in Python?

You can't change str's __add__, but you can define how to add your class to strings. I don't recommend it, though.

class MyClass(object):
...
def __add__(self, other):
if isinstance(other, str):
return str(self) + other
...
def __radd__(self, other):
if isinstance(other, str):
return other + str(self)
...

In "asdf" + thing, if "asdf".__add__ doesn't know how to handle the addition, Python tries thing.__radd__("asdf").

Overloading the [] operator in python class to refer to a numpy.array data member

You need to implement the __getitem__ and __setitem__ magic functions.

A complete overview for the magic methods can be found here.

import numpy as np

class MyArray():
def __init__(self):
self.data = np.zeros(10)

def __getitem__(self, key):
return self.data[key]

def __setitem__(self, key, value):
self.data[key] = value

def __repr__(self):
return 'MyArray({})'.format(self.data)

a = MyArray()

print(a[9])
print(a[1:5])
a[:] = np.arange(10)
print(a)

Which will give you this result:

0.0
[ 0. 0. 0. 0.]
MyArray([ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9.])

Inheritance

If you just want to modify or add behaviour of np.ndarray, you could inherit from it. This is a little more complicated than for normal python classes, but implementing your case should be not that hard:

import numpy as np

class MyArray(np.ndarray):

def __new__(cls, shape, fill_value=0, dtype=float):
data = np.full(shape, fill_value, dtype)
obj = np.asarray(data).view(cls)
obj.fill_value = fill_value
return obj

def reset(self, fill_value=None):
if fill_value is not None:
self.fill_value = fill_value

self.fill(self.fill_value)

For more info, see here.

How do I override the `**` operator used for kwargs in variadic functions for my own user-defined classes?

Implementing .keys() and .__getitem__() will be sufficient to allow an instance of your custom class to be expanded using **.

The relevant parts of the cpython source are in ceval.c which uses _PyDict_MergeEx, and thus dict_merge from dictobject.c which states:


/* We accept for the argument either a concrete dictionary object,
* or an abstract "mapping" object. For the former, we can do
* things quite efficiently. For the latter, we only require that
* PyMapping_Keys() and PyObject_GetItem() be supported.
*/

And indeed, implementing these two methods works as you would expect:

class MyMapping:
def __init__(self, d):
self._d = d

def __getitem__(self, k):
return self._d[k]

def keys(self):
return self._d.keys()

def foo(a, b):
print(f"a: {a}")
print(f"b: {b}")

mm = MyMapping({"a":"A", "b":"B"})
foo(**mm)

Output:


a: A
b: B

Side note: your .keys() implementation need only return an iterable (e.g. a list would be fine), not necessarily a dict_keys object like I do above for simplicity. That line could also have been return list(self._d.keys()) without issue.

Something unusual like the following would also work:

class MyMapping:
def __getitem__(self, k):
return 2

def keys(self):
return ["a", "b", "c"]

def foo(a, b, **kwargs):
print(f"a: {a}")
print(f"b: {b}")
print(f"kwargs: {kwargs}")

mm = MyMapping()
foo(**mm)

Output:


a: 2
b: 2
kwargs: {'c': 2}

Operator overloading in Python: handling different types and order of parameters

You also need to implement __rmul__. When the initial call to int.__mul__(7, v) fails, Python will next try type(v).__rmul__(v, 7).

def __rmul__(self, lhs):
return self * lhs # Effectively, turn 7 * v into v * 7

As Rawing points out, you could simply write __rmul__ = __mul__ for this definition. __rmul__ exists to allow for non-commutative multiplication where simply deferring to __mul__ with the operands reversed isn't sufficient.

For instance, if you were writing a Matrix class and wanted to support multiplication by a nested list, e.g.,

m = Matrix(...)  # Some 2 x 2 matrix
n = [[1, 2], [3,4]]
p = n * m

Here, the list class wouldn't know how to multiple a list by a Matrix instance, so when list.__mul__(n, m) fails, Python would next try Matrix.__rmul__(m, n). However, n * m and m * n are two different results in general, so Matrix.__rmul__(m, n) != Matrix.__mul__(m, n); __rmul__ has to do a little extra work to generate the right answer.

Error when trying to overload an operator /

In Python 3.x, you need to overload the __floordiv__ and __truediv__ operators, not the __div__ operator. The former corresponds to the // operation (returns an integer) and the latter to / (returns a float).

In Python, how to override the arithmetic operator / to produce: 1 / 0 = math.inf?

You need to define your own class and within that define methods __truediv__ (/) and __floordiv__ (//) at a minimum. If you only define those two + for example would not work (see error below).

import math

class MyFloat:
def __init__(self, val):
self.val = val

def __truediv__(self, other):
if other.val == 0:
return math.inf
return self.val / other.val

def __floordiv__(self, other):
if other.val == 0:
return math.inf
return self.val // other.val

one = MyFloat(1)
zero = MyFloat(0)

print(one / zero)
print(one // zero)
// will throw an error (PyCharm will also pick up on this)
print(one + zero)

Expected output

Traceback (most recent call last):
File "/home/tom/Dev/Studium/test/main.py", line 24, in <module>
print(one + zero)
TypeError: unsupported operand type(s) for +: 'MyFloat' and 'MyFloat'
inf
inf

For a list of those special Python function see this website.

Is it possible to override operators (like __add__) in a Python metaclass?

Based on this answer, this is how I did it:

class MyMetaClass(type):

def __add__(self, other):
return "some META funny result"

class MyClass(metaclass=MyMetaClass):
def __add__(self, other):
return "some funny result"

# Works fine: "some funny result"
print(MyClass() + MyClass())

# Works fine: "some META funny result"
print(MyClass + MyClass)

Comprehensive guide to Operator Overloading in Python

I like this reference to quickly see which operators may be overloaded:

http://rgruet.free.fr/PQR26/PQR2.6.html#SpecialMethods

Here is another resource, for completeness (and also for Python 3)

http://www.python-course.eu/python3_magic_methods.php



Related Topics



Leave a reply



Submit