I have a class that dynamically overloads basic arithmetic operators like so…
import operator
class IshyNum:
def __init__(self, n):
self.num=n
self.buildArith()
def arithmetic(self, other, o):
return o(self.num, other)
def buildArith(self):
map(lambda o: setattr(self, "__%s__"%o,lambda f: self.arithmetic(f, getattr(operator, o))), ["add", "sub", "mul", "div"])
if __name__=="__main__":
number=IshyNum(5)
print number+5
print number/2
print number*3
print number-3
But if I change the class to inherit from the dictionary (class IshyNum(dict):) it doesn’t work. I need to explicitly def __add__(self, other) or whatever in order for this to work. Why?
The answer is found in the two types of class that Python has.
The first code-snippet you provided uses a legacy “old-style” class (you can tell because it doesn’t subclass anything – there’s nothing before the colon). Its semantics are peculiar. In particular, you can add a special method to an instance:
and get a valid response:
But, subclassing
dictmeans you are generating a new-style class. And the semantics of operator overloading are different:To make this work with new-style classes (which includes subclasses of
dictor just about any other type you will find), you have to make sure the special method is defined on the class. You can do this through a metaclass:Also, the semantic difference means that in the very first case I could define my local add method with one argument (the
selfit uses is captured from the surrounding scope in which it is defined), but with new-style classes, Python expects to pass in both values explicitly, so the inner function has two arguments.As a previous commenter mentioned, best to avoid old-style classes if possible and stick with new-style classes (old-style classes are removed in Python 3+). Its unfortunate that the old-style classes happened to work for you in this case, where new-style classes will require more code.
Edit:
You can also do this more in the way you originally tried by setting the method on the class rather than the instance:
I’m afraid I sometimes think in Metaclasses, where simpler solutions would be better 🙂