Estoy tratando de sobrecargar varios operadores a la vez usando la función __getattr__ . En mi código, si llamo a foo.__add__(other) funciona como se esperaba, pero cuando intento foo + bar , no funciona. Aquí hay un ejemplo mínimo:
class Foo(): def add(self, other): return 1 + other def sub(self, other): return 1 - other def __getattr__(self, name): stripped = name.strip('_') if stripped in {'sub', 'add'}: return getattr(self, stripped) else: return if __name__=='__main__': bar = Foo() print(bar.__add__(1)) # works print(bar + 1) # doesn't work Me doy cuenta de que sería más fácil en este ejemplo simplemente definir __add__ y __sub__ , pero esa no es una opción en mi caso.
Además, como una pequeña pregunta secundaria, si reemplazo la línea:
if stripped in {'sub', 'add'}:con
if hasattr(self, name):el código funciona, pero luego mi kernel iPython falla. ¿Por qué sucede esto y cómo podría prevenirlo?
Esto sucede porque los operadores de Python usan una optimización para buscar la función que implementa el operador. Las siguientes líneas son aproximadamente equivalentes:
foo + 1 type(foo).__add__(foo, 1)Los operadores se encuentran específicamente solo en el objeto de clase, nunca en la instancia.
bar.__add__(1) llama a __getattr__ para encontrar el atributo que falta en la bar . Esto funciona porque pasa por alto los procedimientos normales de búsqueda del operador.
bar + 1 llama a Foo.__add__(bar, 1) seguido de int.__radd(1, bar) . La búsqueda del primer atributo falla y la segunda opción genera TypeError .
Además, como una pequeña pregunta secundaria, si reemplazo la línea: [...]
hasattr llama a __getattr__ debajo del capó. Lo que explica lo que vio al hacer if hasattr(self, name): , en realidad ingresa una recursividad infinita ya que ha sobrescrito __getattr__ .
Ver por ti mismo
class O: def __getattr__(self, attr): print('yooo') return super().__getattr__(attr) # self.__getattr__(attr) -> RecursionError >>> hasattr(O(), 'ya') yooo FalseEncontré una solución semi-satisfactoria que no involucra metaclases:
class Foo(): def add(self, other): return 1 + other def sub(self, other): return 1 - other # Add operators to the type object def get_operator(name): return getattr(Foo, name.strip('_')) for op in ['__add__', '__sub__']: setattr(Foo, op, get_operator(op)) if __name__=='__main__': bar = Foo() print(bar + 2) print(bar - 2)