Override Python's 'In' Operator

Override Python's 'in' operator?

MyClass.__contains__(self, item)

How do I override python's not in operator

There's no separate "notcontains" hook. in and not in cannot be overridden separately.

Separate __eq__ and __ne__ hooks exist because == and != might not return booleans. For example, you can't implement != for NumPy arrays as not (x == y). not in doesn't have that issue, because both in and not in must return booleans.

If you look at the data model documentation, you'll see that it only documents a single __contains__ hook for both in and not in. You can also take a look at the implementation, where both in and not in call the same PySequence_Contains C API function and then not in applies ! to the result:

    case PyCmp_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
break;
case PyCmp_NOT_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
res = !res;
break;

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}

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__'

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").

Any way to override the and operator in Python?

You cannot override the and, or, and not boolean operators.

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.

python: overload operator with different types

If you want to compare a CRational object with an int then your __gt__ method should works with integers too. I.e., if other is an integer, you clearly can't do something like other.b. Here is a possible solution:

class CRational:
def __init__(self, a = 0, b = 1):
self.a = a
self.b = b
def __gt__(self, other):
if isinstance(other, CRational):
return self.a * other.b > other.a * self.b
elif isinstance(other, int):
# Compare self (a CRational object) with other (an int)
# TODO
else:
raise NotImplemented()

Now you can do something like this:

a = CRational()
if a > 3:
...

Be careful though! Even if you implement correctly all the methods, you still can't do 3 > a. Order is important!! 3 > a would call the __gt__ method of the int class. You can only do a > 3, a < 3, a >= 3 etc.



Related Topics



Leave a reply



Submit