multiple method init__ herency comando __init__ python super

method - super class self__ init__ python



¿Por qué tenemos que usar los métodos__dunder__ en lugar de los operadores cuando llamamos por super? (3)

El error 805304 del rastreador de errores de CPython, "las superinstancias no son compatibles con la asignación de elementos", ha proporcionado a Raymond Hettinger una explicación detallada de las dificultades percibidas.

La razón por la que esto no funciona automáticamente es que dichos métodos deben definirse en la clase debido al almacenamiento en memoria caché de los métodos de Python, mientras que los métodos por proxy se encuentran en tiempo de ejecución.

Él ofrece un parche que le daría un subconjunto de esta funcionalidad:

+ if (o->ob_type == &PySuper_Type) { + PyObject *result; + result = PyObject_CallMethod(o, "__setitem__", "(OO)", key, value); + if (result == NULL) + return -1; + Py_DECREF(result); + return 0; + } +

así que es claramente posible .

Sin embargo, concluye

He estado pensando que podría dejarlo solo y solo documentar que los súper objetos solo hacen su magia en la búsqueda de atributos explícitos.

De lo contrario, arreglarlo por completo implica combinar Python para cada lugar que llame directamente a las funciones de la tabla de ranuras, y luego agregar una llamada de seguimiento mediante la búsqueda de atributos si la ranura está vacía.

Cuando se trata de funciones como repr (obj), creo que queremos que el superobjeto se identifique a sí mismo en lugar de reenviar la llamada al método __repr __ () del objeto de destino.

El argumento parece ser que si los métodos __dunder__ son proxys, entonces __repr__ se aproxima o hay una inconsistencia entre ellos. super() , por lo tanto, podría no querer usar dichos métodos para evitar que se acerque demasiado al equivalente del programador de un valle extraño.

¿Por qué tenemos que usar __getitem__ lugar del acceso habitual del operador?

class MyDict(dict): def __getitem__(self, key): return super()[key]

Obtenemos TypeError: ''super'' object is not subscriptable .

En su lugar, debemos usar super().__getitem__(key) , pero nunca entendí por qué: ¿qué es exactamente lo que impidió que el súper se implementara de forma que permitiera el acceso del operador?

Subscriptable fue solo un ejemplo, tengo la misma pregunta para __getattr__ , __init__ , etc.

Los docs intentan explicar por qué, pero no lo entiendo.


Lo que pides se puede hacer, y fácilmente. Por ejemplo:

class dundersuper(super): def __add__(self, other): # this works, because the __getattribute__ method of super is over-ridden to search # through the given object''s mro instead of super''s. return self.__add__(other) super = dundersuper class MyInt(int): def __add__(self, other): return MyInt(super() + other) i = MyInt(0) assert type(i + 1) is MyInt assert i + 1 == MyInt(1)

Entonces, la razón por la que Super funciona con métodos mágicos no es porque no sea posible. La razón debe estar en otra parte. Una razón es que hacerlo violaría el contrato de iguales ( == ). Eso es igual, entre otros criterios, simétrico. Esto significa que si a == b es verdadero, entonces b == a también debe ser verdadero. Eso nos lleva a una situación complicada, donde super(self, CurrentClass) == self , pero self != super(self, CurrentClass) por ejemplo.

class dundersuper(super): def __eq__(self, other): return self.__eq__(other) super = dundersuper class A: def self_is_other(self, other): return super() == other # a.k.a. object.__eq__(self, other) or self is other def __eq__(self, other): """equal if both of type A""" return A is type(self) and A is type(other) class B: def self_is_other(self, other): return other == super() # a.k.a object.__eq__(other, super()), ie. False def __eq__(self, other): return B is type(self) and B is type(other) assert A() == A() a = A() assert a.self_is_other(a) assert B() == B() b = B() assert b.self_is_other(b) # assertion fails

Otra razón es que una vez que se hace una superación al buscar el mro de un objeto dado, se debe dar la oportunidad de proporcionar el atributo solicitado (los super objetos siguen siendo un objeto por derecho propio), deberíamos ser capaces de probar la igualdad con otros objetos, solicite representaciones de cadenas e introspectivamente el objeto y clase con la que está trabajando super. Esto crea un problema si el método dunder está disponible en el super objeto, pero no en el objeto que representa el objeto mutable. Por ejemplo:

class dundersuper(super): def __add__(self, other): return self.__add__(other) def __iadd__(self, other): return self.__iadd__(other) super = dundersuper class MyDoubleList(list): """Working, but clunky example.""" def __add__(self, other): return MyDoubleList(super() + 2 * other) def __iadd__(self, other): s = super() s += 2 * other # can''t assign to the result of a function, so we must assign # the super object to a local variable first return s class MyDoubleTuple(tuple): """Broken example -- iadd creates infinite recursion""" def __add__(self, other): return MyDoubleTuple(super() + 2 * other) def __iadd__(self, other): s = super() s += 2 * other return s

Con el ejemplo de la lista, la función __iadd__ podría haberse escrito más simplemente como

def __iadd__(self, other): return super().__iadd__(other)

Con el ejemplo de la tupla caemos en una recursión infinita, esto es porque tuple.__iadd__ no existe. Por lo tanto, cuando se busca el atributo __iadd__ en un súper objeto, se __iadd__ el súper objeto real para un atributo __iadd__ (que existe). Obtenemos ese método y lo llamamos, lo que inicia todo el proceso nuevamente. Si no hubiéramos escrito un método __iadd__ en super y super().__iadd__(other) entonces esto nunca hubiera sucedido. Más bien, obtendríamos un mensaje de error sobre un súper objeto que no tiene el atributo __iadd__ . Ligeramente críptico, pero menos que una traza de pila infinita.

Entonces, la razón por la que el súper no funciona con los métodos mágicos es que crea más problemas de los que resuelve.


Los métodos Dunder deben definirse en la clase, no en la instancia. super () necesitaría tener una implementación de cada método mágico para hacer que esto funcione. No vale la pena escribir todo ese código y mantenerlo actualizado con la definición del lenguaje (por ejemplo, la introducción de la multiplicación de matrices en 3.5 creó tres nuevos métodos dunder), cuando puedes simplemente decirles a los usuarios que escriban los métodos dunder a mano. Utiliza la búsqueda normal de métodos, que se puede emular fácilmente.