1

Here is what you would expect if you try and use the add operand over a type and int or between two type objects.

>>> class Foo:
...     pass
...
>>> class Bar:
...     pass
...
>>> Foo + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'type' and 'int'
>>> Foo + Bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'type' and 'type'

What I would like to know if, is there a way to allow the support of operands between types in a kind of straight forward way.

The only work around that I can think of to implement an interface like this is to implement a class overwriting the __call__ method, so each object of that class can actually instantiate classes from other types. That way you can "mimic" the construction by doing something like this.

class TypeArithmetic:
     def __init__(self, wrap):
          self.wrap

     def __call__(self, *args, **kwargs):
         return self.wrap(*args, **kwargs)

     def __add__(self, other):
         return 1 # Calculate something here


@TypeArithmetic
class CLASS_1:
    pass

@TypeArithmetic
class CLASS_2:
    pass

CLASS_1 + CLASS_2 # according to the __add__ here, this returns 1.

More or so.

Is there a way of achieving this without the implementation of a similar decorator?

ekiim
  • 792
  • 1
  • 11
  • 25
  • Please update your question with the exact return values from all the combinations of operands of `+`. – quamrana Sep 27 '22 at 21:03
  • 2
    The reason it's not straight forward is because addition would be meaningless for most types, therefore throwing an error is the most sensible default behavior. – luther Sep 27 '22 at 21:11
  • @quamrana, Essentially I'm looking for a way to bind a function to the operand, I don't see how the returned values is relevant, could you elaborate on that part? – ekiim Sep 27 '22 at 21:39
  • 1
    While throwing an error is reasonable, isn't this exactly the use case for which a metaclass is appropriate? – iteratedwalls Sep 27 '22 at 21:40
  • 1
    @ekiim following up on my previous comment, here's a metaclass solution. – iteratedwalls Sep 27 '22 at 22:30
  • What do you want `Foo + 1` or `Foo + Bar` to produce? You need to decide that before you worry about whether implementing type addition is desirable or necessary. – chepner Sep 27 '22 at 22:42
  • In most cases, I need it to return another class, or boolean values depending on the comparison criterias between them. – ekiim Sep 27 '22 at 22:50
  • If you just want a method, then the meta class below works. – quamrana Sep 28 '22 at 06:56

1 Answers1

1

You can use a metaclass which defines the operators on instances of itself.

class Meta(type):
    def __add__(self, other):
        return self, other

class Foo(metaclass=Meta):
    pass

print(Foo+int) # (<class '__main__.Foo'>, <class 'int'>)

How does it work? All classes are instances of a metaclass, of which type is one, and since classes are metaclass instances, metaclasses can define methods that can be called on the class, just as ordinary classes can define methods that can be called on their instances, because classes are objects in Python.

iteratedwalls
  • 223
  • 1
  • 9
  • Is there deeper documentation on the internals of how this works? If I understand correct, It shouldn't be as different as the decorator I was using right? – ekiim Sep 27 '22 at 22:32
  • https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python is my go-to reference on how metaclasses work. If you really want to go into exceptional detail, I would suggest joining the Python Discord and asking some of the members about the CPython Class Creation Chart, which is a very in-depth analysis of how classes are created in Python. – iteratedwalls Sep 27 '22 at 22:34
  • 2
    @ekiim no it isn't like your decorator. Your decorator wraps your type in another class in another object which implements `__add__`, a metaclass **is the class of your class**, so with the metaclass, your class implements `__add__` natively. – juanpa.arrivillaga Sep 27 '22 at 22:41