Question

I am using python 3 (3.3, but I'm interested of any nice solution, even if it's only python 3.4 or more). I want to create a custom class with conditional overloading operator like that:

class test :
    def __init__(self,overload) :
         if overload :
             self.__add__=lambda x,y : print("OK")
             self.f=lambda : print(True)
    def f(self) : print(False)

t=test(False)
t.f() #print False as expected
u=test(True)
u.f() #print True as expected
u+u #fails as if it was not implemented

After some digging, I realized that operator + does not call __add__ on the object, but on the class. Hence, I can't do it like that (or can I)?

So I can define something like that :

class test :
    def __init__(self,overload) :
        self.overload=overload
    def __add__(self,other) :
        if self.overload : print("OK")
        else #FAILURE
    def f(self) : print(self.overload)

But what can I put into the FAILURE statement such that it fails exactly like if it was not implemented? For example, if it fails, I'll be happy if it tried __radd__ on the second operator.

Was it helpful?

Solution

Indeed, special methods are looked up on the type and you cannot set them on the instance.

If you want to not always support .__add__ return the NotImplemented singleton:

class test:
    def __init__(self, overload):
        self.overload = overload

    def __add__(self, other):
        if not self.overload:
            return NotImplemented
        print("Ok")

Returning NotImplemented from a rich comparison method makes Python behave as if there was no such method; usually this is used to signal that the type of other isn't supported, but you are free to return it when you don't want to support the operation just now.

Note that Python will still try the reverse operation; for test() + something, Python first tries test().__add__(something), then something.__radd__(test()) is called if the first operation returned NotImplemented. If the latter doesn't exist or also returns NotImplemented, a TypeError is raised.

Demo:

>>> class test:
...     def __init__(self, overload):
...         self.overload = overload
...     def __add__(self, other):
...         if not self.overload:
...             return NotImplemented
...         print("Ok")
... 
>>> test(False) + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'instance' and 'int'
>>> test(True) + 1
Ok
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top