Package: mathdunders
mathdundersA Python decorator that automatically adds math-related dunder methods to a class derived from a numeric type.Useful when you want operations on that class to remain the same type but don't want to manually write all the dunders.Think of it in the same vein as@functools.total_ordering.Installationpip install mathdundersView on PyPI-View on GitHubThis package was built in Python 3.9.4 and tested to work in 3.6.8+.UsageImport and put@mathdunders()above your class:frommathdundersimportmathdunders@mathdunders()classRealNumber(float):passa=RealNumber(3)+RealNumber(4)print(a,type(a))# -> 7.0 <class '__main__.RealNumber'>b=RealNumber(3)*4print(b,type(b))# -> 12.0 <class '__main__.RealNumber'>c=3-RealNumber(4)print(c,type(c))# -> -1.0 <class '__main__.RealNumber'>Now any math operation withRealNumberwill result in anotherRealNumberrather than a float.In the above code block,@mathdunders()makesRealNumberbehave as if it was written like:classRealNumber(float):def__add__(self,other):returnRealNumber(float(self)+other)def__mul__(self,other):returnRealNumber(float(self)*other)def__rsub__(self,other):returnRealNumber(other-float(self))# ... plus 20 other similar boilerplate dunder methodsSupported Dunders@mathdunders()adds 23 "magic" double-underscore (dunder) methods to the class it decorates:Dunder           Trigger
__abs__          abs(x)
__ceil__         math.ceil(x)
__floor__        math.floor(x)
__neg__          -x
__pos__          +x
__round__        round(x)
__trunc__        math.trunc(x)
__add__          x + 3
__divmod__       divmod(x, 3)
__floordiv__     x // 3
__mod__          x % 3
__mul__          x * 3
__pow__          x ** 3
__sub__          x - 3
__truediv__      x / 3
__radd__         3 + x
__rdivmod__      divmod(3, x)
__rfloordiv__    3 // x
__rmod__         3 % x
__rmul__         3 * x
__rpow__         3 ** x
__rsub__         3 - x
__rtruediv__     3 / xdundersis a tuple of all supported dunder names:frommathdundersimportdundersprint(dunders)# -> ('__abs__', '__ceil__', '__floor__', '__neg__', ...Comparison operators are omitted because it doesn't make sense for them to return the decorated type.Bitwise operators are omitted because they are not well defined for many numeric types, e.g. floats.Note that__ceil__and__floor__are unimplemented for floats in Python versions before 3.9.Advanced UsageWith Multiple InheritanceIf the base type such asfloat,int, orDecimalis not the first base class, use the optionalbaseparameter to specify it.frommathdundersimportmathdundersclassParent:pass@mathdunders(base=int)classInt(Parent,int):passprint(Int(10)/Int(2))# -> 5Overwrite Existing DundersBy default dunders are not inserted if the class already defines them. Set the optional parameterforcetoTrueto override this.frommathdundersimportmathdunders@mathdunders(force=False)# default behaviorclassA(float):def__abs__(self):return1234a=abs(A(-1))print(a,type(a))# -> 1234 <class 'int'>@mathdunders(force=True)# forces dunders to be overwrittenclassB(float):def__abs__(self):return1234b=abs(B(-1))print(b,type(b))# -> 1.0 <class '__main__.B'>Supplying Custom DundersIf you wish to supply a custom set of dunders you may use the optionaldundersparameter.frommathdundersimportmathdunders,dundersextras=('__and__','__lshift__')@mathdunders(dunders=dunders+extras)classInt(int):passa=Int(2)&Int(3)print(a,type(a))# -> 2 <class '__main__.Int'>b=Int(2)|Int(3)print(b,type(b))# -> 3 <class 'int'>c=Int(1)<<4print(c,type(c))# -> 16 <class '__main__.Int'>