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:
"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á. O
se comprueba a su vez, buscando "A"
en cada uno de sus dictados. Si lo encuentra, llame al valor D
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:
__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]
.
super().__new__(cls)
expande a super(CarModel, cls).__new__(cls)
, como se describió anteriormente. super(CarModel, cls)
se evalúa para producir un super objeto S
"__new__"
en S
(el equivalente a llamar a getattr(S, "__new__")
en el código de Python). 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__
. 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 .