Supongamos que construyo una clase que básicamente representa un número más algunas cosas sofisticadas. Las instancias de esa clase deben comportarse como números en cualquier operación aritmética/matemática.
Podría sobrecargar todos los operadores numéricos de esa clase, pero ¿no hay una solución más corta?
La clase básicamente se parece a:
class MyFancyNumber: def __init__(self, num, info): self.num = num # the actual number self.info = info # some more info, or other data def doFancyStuff(self): # does something fancy def __add__(self, other): return self.num + other # same pattern for all numeric functions¿Qué pasa con esto?
class MyFancyNumber(int): def __new__(cls, num, info=None): return super(MyFancyNumber, cls).__new__(cls, num) def __init__(self, num, info=None): self.num = num self.info = info >>> MyFancyNumber(5) 5 >>> MyFancyNumber(5) + 2 7 >>> MyFancyNumber(5) / 4 1 >>> MyFancyNumber(5) * 0.5 2.5 >>> MyFancyNumber(5) - 7 -2 >>> MyFancyNumber(5, 'info').info 'info'Supongo que en base a lo anterior, puedes averiguar lo que necesitas.
No apruebo esto por ser particularmente idiomático, pero...
Suponiendo que todas las definiciones de funciones se comporten de manera idéntica, como "simplemente invoque el comportamiento base de la clase self.num y aplíquele todos los argumentos que no son propios", entonces puede recorrer todos los nombres de funciones que desea definir y crear cada uno usando setattr . Ejemplo:
class MyFancyNumber(object): def __init__(self, num, info): self.num = num self.info = info def __repr__(self): return "MyFancyNumber({}, {})".format(repr(self.num), repr(self.info)) def make_func(name): return lambda self, *args: MyFancyNumber(getattr(self.num, name)(*args), self.info) for name in ["__add__", "__sub__", "__mul__", "__div__", "__invert__", "__neg__", "__pos__"]: setattr(MyFancyNumber, name, make_func(name)) x = MyFancyNumber(50, "hello") print(x + 10) print(x - 10) print(x * 10) print(x / 10) print(~x) print(-x) print(+x)Resultado:
MyFancyNumber(60, 'hello') MyFancyNumber(40, 'hello') MyFancyNumber(500, 'hello') MyFancyNumber(5, 'hello') MyFancyNumber(-51, 'hello') MyFancyNumber(-50, 'hello') MyFancyNumber(50, 'hello')Editar: no estaba seguro de si quería que el resultado de la aritmética fuera MyFancyNumber o un tipo numérico incorporado regular, pero de cualquier manera, la implementación es bastante similar:
class MyFancyNumber(object): def __init__(self, num, info): self.num = num self.info = info def __repr__(self): return "MyFancyNumber({}, {})".format(repr(self.num), repr(self.info)) def make_func(name): return lambda self, *args: getattr(self.num, name)(*args) for name in ["__add__", "__sub__", "__mul__", "__div__", "__invert__", "__neg__", "__pos__"]: setattr(MyFancyNumber, name, make_func(name)) x = MyFancyNumber(50, "hello") print(x + 10) print(x - 10) print(x * 10) print(x / 10) print(~x) print(-x) print(+x)Resultado:
60 40 500 5 -51 -50 50No, debe definir todos los operadores aritméticos, de lo contrario, ¿cómo sabría Python qué hacer con ellos? No olvides que también necesitas los operadores inversos como __radd__ .
Además, el código que ha escrito devuelve un int para x+1 . ¿Quiso decir eso o quería agregar un número elegante para devolver otro número elegante?
Podrías simplemente subclasificar int o float . Entonces no tiene que volver a implementar los operadores, pero aún perderá la naturaleza especial cada vez que opere en un valor.
Una mejor solución sería simplemente tener el valor numérico en un atributo y convertirlo a números explícitamente cuando eso es lo que desea. Puede usar __int__() y __float__() para implementar la conversión.
La documentación cubre lo que debe hacer si realmente desea emular un tipo numérico: para Python 3.x https://docs.python.org/3/reference/datamodel.html?highlight= int #emulator-numeric- tipos o para Python 2.x https://docs.python.org/2/reference/datamodel.html?highlight= int #emulator-numeric-types