5

Soy nuevo en Django y Python en general y me ha surgido una duda en la siguiente línea de código:

queryset = super(EjemploListView, self).get_queryset()

no tengo claro que función tiene super en ella.

El código de la clase dónde está esta línea es el siguiente:

class EjemploListView(ListView):
    model = Ejemplo
    def get_queryset(self):
        queryset = super(EjemploListView, self).get_queryset()
        filter = self.request.GET.get('filter')
        if filter == 'add':
           queryset = queryset.filter(data=0)
        elif filter== 'del':
           queryset = queryset.filter(data=1)
        return queryset
FJSevilla
  • 55,603
  • 7
  • 35
  • 58
NEFEGAGO
  • 428
  • 4
  • 20

2 Answers2

15

super (nombre que deriva de "superclass") tiene la siguiente sintaxis general:

super(subClase, instancia).método(argumentos)

En Python 3 tanto la clase como la instancia se pasan de forma automática a super, por lo que se puede hacer simplemente:

super().método(argumentos)

Básicamente es un shortcut que permite acceder a la clase base de una clase derivada y delegar llamadas a métodos de esta, sin tener que conocer ni escribir el nombre de la clase base. Retorna un objeto proxy, que permite actuar como un camino de llamada a métodos de alguna clase padre, no retorna una referencia a la clase padre en si misma.

En este caso se usa para llamar al método get_queryset() de la clase padre ListView.

Podemos verlo con un ejemplo muy simple:

class Padre:
    def foo(self):
        return 13


class Hija(Padre):
    def foo(self):
        return 0

    def bar(self):
        n = super(Hija, self).foo()
        return n

inst = Hija()
print(inst.bar()) # 13
print(inst.foo()) # 0

Como se puede ver, con super(Hija, self).foo() se llama la método foo de la clase padre independientemente de que lo hemos sobrescrito en la clase hija. Esto es muy importante ala hora de llamar al inicializador de una clase padre y pasarle los argumentos necesarios al instanciar una clase hija.

En el caso de la herencia simple, como en este caso, sería equivalente a n = Padre.foo(self). La principal ventaja es no tener que especificar el nombre de la clase padre, lo que facilita el mantenimiento del código.


En la herencia múltiple, en cambio, es donde muestra su verdadero potencial y tiene una relación intima con el método de resolución del orden de la herencia. Veamos un ejemplo muy simple:

class A:
    def foo(self):
        print("Método foo desde clase A")


class B(A):
    def foo(self):
        print("Método foo desde clase B")


class C(A):
    pass


class D(C, B):
    def bar(self):
        super(D, self).foo()

inst = D()
inst.bar() # Método foo desde clase B

En este caso D hereda de C y de B, que a su vez heredan de A. Al hacer super().foo() desde D nos encontramos con que se llama al método foo de la clase B ¿Por qué?

Bien, Python tiene un mecanismo conocido como MRO (Method Resolution Order) que especifica el orden de la herencia y que determina que método heredar si tenemos dos clases padre con el mismo método entre otras cosas, por ejemplo D hereda el método foo de C, no de B o A. Podemos ver este orden consultando el atributo __mro__ que tiene toda clase.

super busca el primer método foo que encuentra entre las clases de las que hereda siguiendo el orden establecido por el MRO. En nuestro caso, el MRO indica lo siguiente:

>>> print([clase.__name__ for clase in D.__mro__])
>>> ['D', 'C', 'B', 'A', 'object']

Por tanto, primero busca en C, que no implementa el método (solo lo hereda de A). Al no encontrarlo en C, lo busca en B en el que si lo encuentra y lo llama. Si no lo encontrara pasaría a A y seguiría hasta llegar a object. La herencia múltiple es un tema complejo y que se sale del ámbito de la pregunta, pero esto es un ejemplo del papel de super en ella y que justifica su existencia. Uno de los problemas más conocidos que trata de resolver super es el llamado "problema del diamante".

FJSevilla
  • 55,603
  • 7
  • 35
  • 58
1

Significa que está llamando al método get_queryset() de la superclase ListView. Es herencia de clases: EjemploListView hereda de ListView el método getqueryset(), pero se vuelve a implementar dicho método en EjemploListView para sobreescribir su comportamiento, por lo que para llamar al método de la superclase con el mismo nombre debes usar super() o se llamará recursivamente a sí mismo desde dentro del método una y otra vez.

No sólo aparece en Django sino que es una función del lenguaje Python. Aquí puedes ver más información.