I am trying to create a Python class that does operations mod n. For example using mod 100:
11*11==21
66+39==5
1+2+3-30==73
2**10==24
This is how I am doing it:
class ModInteger:
def __init__(self, value, mod):
self.mod = mod
self.value = value % mod
def __add__(self, other):
return ModInteger((self.value + other.value) % self.mod, self.mod)
def __sub__(self, other):
return ModInteger((self.value - other.value) % self.mod, self.mod)
def __mul__(self, other):
return ModInteger((self.value * other.value) % self.mod, self.mod)
def __pow__(self, other):
return ModInteger((self.value ** other.value) % self.mod, self.mod)
#...
It works, but is there a less tedious and repetitive way of doing it?
A more generic approach would be to build ModInteger
with a metaclass that creates wrapper methods around relevant int
methods.
To allow compatibility with both int
and ModInteger
objects the wrapper methods should convert arguments of ModInteger
objects into int
objects before passing them to int
methods, and convert returning values back to ModInteger
objects if they are int
.
The demo below shows how wrapper methods work for both unary and binary operations, as well as methods such as __repr__
, which doesn't take an argument and doesn't return an int
object:
class ModIntegerMeta(type):
def __new__(metacls, name, bases, members):
cls = super().__new__(metacls, name, bases, members)
for name in '__add__', '__sub__', '__mul__', '__neg__', '__repr__':
setattr(cls, name, cls.create_method(name))
return cls
def create_method(cls, name):
def method(self, *args):
value = getattr(self.value, name)(*(
arg.value if isinstance(arg, cls) else arg for arg in args
))
if type(value) is int:
return cls(value, self.mod)
return value
return method
class ModInteger(metaclass=ModIntegerMeta):
def __init__(self, value, mod):
self.mod = mod
self.value = value % mod
print((-ModInteger(66, 100) + ModInteger(39, 100) * 5))
This outputs 29 because (-66 + (39 * 5) % 100) % 100 == 29