¿Qué hace `super ()` en `__new__`

Nota: Parte de una implementación de peso mosca con Python

import weakref class CarModel: _models = weakref.WeakValueDictionary() def __new__(cls, model_name, *args, **kwargs): model = cls._models.get(model_name) if not model: model = super().__new__(cls) cls._models[model_name] = model return model def __init__(self, model_name, air=False): if not hasattr(self, "initted"): self.model_name = model_name self.air = air self.initted=True 

Pregunta 1> ¿Qué significa super() ? ¿Significa la clase padre de CarModel ?

Pregunta 2> ¿También tengo dificultades para entender cómo funciona la función __new__ ? En concreto, la siguiente línea.

 model = super().__new__(cls) 

Descripción para __new__ :

La función constructora se llama __new__ en lugar de __init__ , y acepta exactamente un argumento, la clase que se está construyendo (se llama antes de que se construya el objeto, por lo que no hay argumento propio). También tiene que devolver el objeto recién creado.

Comenzar con super() en sí mismo es simplemente una abreviatura de super(A, B) , donde A es la clase en la que se produce el código, y B es el primer argumento de la función en la que se produce el código; así que en su caso particular, super().__new__(cls) expande a super(CarModel, cls).__new__(cls) .

A su vez, super(T, O) devuelve un “súper objeto”. Para comprender qué hace un súper objeto, debe comprender cómo funcionan las referencias de atributos en instancias y clases en Python.

Suponiendo que no __getattr__ __getattribute__ métodos __getattr__ o __getattribute__ , hacer referencia al atributo A en un objeto O (es decir, evaluar OA o getattr(O, "A") ) continúa a través de los siguientes pasos:

  1. Si "A" se define en el dict de instancia de O.__dict__ ( O.__dict__ ), entonces el valor de ese dictado se devuelve directamente, tal como está.
  2. De lo contrario, cada una de las clases en el orden de resolución del método de O se comprueba a su vez, buscando "A" en cada uno de sus dictados. Si lo encuentra, llame al valor D
  3. Si D , a su vez, no define un __get__ , se devuelve tal como está. Sin embargo, si lo hace, se hace referencia a D como un “descriptor”, y su método __get__ se llama con O como primer argumento, y type(O) como segundo argumento.

Una referencia de atributo en una clase funciona de la misma manera, sustituyendo la clase como referencia para la instancia, con las siguientes diferencias:

  • El paso 1 no se aplica.
  • El método __get__ se llama con None como el primer argumento y la clase a la que se hace referencia como el segundo.

Python usa descriptores para implementar cosas como métodos de instancia, métodos de clase, métodos estáticos y propiedades.

Un súper objeto creado con super(T, O) , entonces, es un objeto (incorporado) con un método __getattribute__ que se llama en cada referencia de atributo en él, y busca el atributo en los dictados de las únicas clases que siguen a T en O ‘s MRO. El valor que encuentra, llama a __get__ como de costumbre.

El proceso es un poco complejo, así que a modo de ejemplo, aquí le explicamos cómo funcionaría en su caso específico. Como CarModel se define tal como es, su MRO es [CarModel, object] .

  1. super().__new__(cls) expande a super(CarModel, cls).__new__(cls) , como se describió anteriormente.
  2. super(CarModel, cls) se evalúa para producir un super objeto S
  3. Python recupera el atributo "__new__" en S (el equivalente a llamar a getattr(S, "__new__") en el código de Python).
  4. Dado que S se creó en la clase CarModel , considera las clases que siguen a CarModel en el MRO de CarModel , y encuentra "__new__" en el dict de la propia clase de object . Su valor, un método estático, tiene un método __get__ , que se llama con los argumentos None y cls . Dado que __new__ es un método estático, su método __get__ simplemente devuelve la función tal como está, sin modificaciones. Por lo tanto, super(CarModel, cls).__new__ es exactamente lo mismo que object.__new__ .
  5. La función obtenida en el último paso (es decir, el object.__new__ ) se llama con el argumento cls , donde cls es probablemente CarModel , finalmente una nueva instancia de la clase CarModel .

Espero que eso sea del todo comprensible.

(En aras de la integridad, se debe mencionar que la función real __new__ en la clase de object no es en realidad un método estático, sino una función especial incorporada que simplemente no tiene ningún método __get__ , pero dado que el método __get__ es estático los métodos simplemente devuelven la función con la que se definieron, el efecto es el mismo.)

super() se utiliza para hacer referencia a la superclase (es decir, la clase principal de la que está creando subclases).

__new__ es un método que se invoca para crear una nueva instancia de la clase, si está definida.

Creo que estás usando Python3, donde el super no necesita que se le proporcione el mismo nombre de clase. Super se refiere a las clases base de la clase actual y realiza la Orden de resolución de métodos adecuada para que invoque el método desde la clase base correcta. __new__ es el método que se invoca para crear una instancia. Es el primer paso en la creación de la instancia .