MÉTODOS ESPECIALES


PINOS CANARIOS SOBRE SUELO DE CENIZA Y PICÓN EN MONTAÑAS NEGRAS, PARTE ALTA DE LOS MUNICIPIOS DE SANTIAGO DEL TEIDE Y EL TANQUE, OESTE DE TENERIFE.


      Imaginémonos la siguiente situación: después de mucho tiempo y esfuerzo, usted mismo, accede a una plaza de profesor/a en una universidad de prestigio, pongamos por nombre, la Universidad Nacional de Pythonia. Un gran día: su sueño de dedicarse a la enseñanza académica se ha cumplido. Llega usted tan campante a la facultad que le corresponde (probablemente, Informática y Ciencias de la Información) y recibe los parabienes del decano/a, del resto de profesores del departamento así como de otras facultades asociadas y del  personal adjunto y administrativo poniendo a nuestra disposición medios y recursos para desarrollar nuestro trabajo. Pero en esta Universidad de Pythonia cuentan con una rectora, doña Sophia, que tiene fama de ser muy cercana. Tiene su particular mérito y su peculiar enjundia. No es tan frecuente que la persona con más alta responsabilidad (nadie hay por encima de ella en la jerarquía de la Universidad Nacional de Pythonia) se preocupe tanto de sus subordinados. Es más, nada más acceder a su flamante despacho, allí le espera doña Sophia, tan pintiparada, tan lustrosa, para darle personalmente la bienvenida. Y aún más: le informa con la mejor de sus sonrisas bífidas que para llevar a cabo su trabajo le proporcionará acceso a recursos exclusivos del rectorado: "Todo por el bien de mis profesores y alumnos. Tan sólo tiene que pedírmelos por la clave de acceso que yo misma le voy a proporcionar, y podrá hacer uso de ellos cuando quiera y donde quiera".
¿Puede usted, joven profesor, pedir más?



Ahora vamos a traducir.
El tiempo y el esfuerzo es aquéllo que hemos invertido para adquirir conocimientos de programación con el lenguaje. Nuestra plaza de profesor es nuestro "título" como programadores en este mismo lenguaje. Qué duda cabe, la Universidad Nacional de Pythonia es, ... éso: Python y su ecosistema. Los medios y recursos de nuestro departamento representarían los propios recursos del lenguaje (la sintaxis del lenguaje, clases, librería propias, módulos, etc.) cuyo decano/a no sería otra cosa que la propia documentación de Python, y el resto del profesorado, hombres y mujeres como nosotros, compañeros/as programadores en ese mismo lenguaje, mientras que el resto de facultades podrían asociarse a librerías externas o de terceros, compiladores, etc. 
Y luego está nuestra rectora, Sophia.
¡Ah, Sophia!
¿Qué o quién podría ser Sophia? Sophia es un objeto. Pero no es un objeto cualquiera, no, en absoluto: es un superobjeto. De hecho es la cima de todo, el objeto más importante por encima del cual no existe nada más en Python. De hecho, a decir de algunos iluminados, es Python en sí mismo.
De hecho, Sophia es el objeto por antonomasia:



El tipo más básico, la columna vertebral de la Universidad Nacional de Pythonia.
Pero... ¿y cuáles son estos recursos que menciona tan noble dama y que se ofrece a poner a nuestra disposición con tanta generosidad? Pues el motivo de este capítulo: a tan especial señora, sus no menos métodos especiales. ¿Y esa "clave de acceso"? Su particular ortografía: un doble guión bajo antes y después del nombre del método.





¡Ah! Pero esto del doble guión bajo delante y detrás del nombre del método a qué nos suena? ¿No lo hemos visto ya?... A ver,... ¡Ah, sí! los métodos constructores e inicializadores de clase: __new__() e __init__().


Pues, sí: los métodos __new__() e __init__() son métodos especiales, dos entre unos cuantos más que veremos a continuación.
Empezamos con ellos mostrando en la tabla siguiente los métodos especiales fundamentales:



A continuación, hablaremos un poco de los métodos especiales de clases. Python (nuestra Sophia) nos proporciona un conjunto selecto de métodos especiales que, de manera predeterminada, van a almacenar todas las clases que vayamos instanciando, de tal modo que cada clase que hayamos creado e, incluso, las clases preinstanciadas en Python y a las que podemos invocar directamente, como es el caso de list, str o set, por ejemplo, como mostramos a continuación:



Todas estas clases son, en última instancia, clases "hijas" (subclases) de la clase especial integrada en Python (superclase) object. Esta clase especial object cuenta con sus propios métodos (los recursos de Sophia), los llamados métodos especiales, que pasan a adquirir también nuestras clases instanciadas, que heredan directamente de object,  por así decirlo, sin que nosotros tengamos que hacer nada, aunque podemos sobreescribir los métodos especiales y suplir su funcionalidad por la que nosotros deseemos. Además, podemos implementarlos, si queremos, todos o unos pocos (o, incluso, ninguno) en nuestras propias clases para servirnos de ellos: pueden ayudarnos pero no son imprescindibles, y están enteramente a nuestra disposición con solo invocarlos con la ortografía adecuada.
La inclusión en nuestras clases de métodos especiales permite  llamar posteriormente, de manera sencilla y directa, a la función integrada u operador correspondiente. Por ejemplo, el recurso al método especial __repr__() permite llamar luego a la función integrada repr(), de la misma manera que el recurso al método especial de comparación __gt__(), permite invocar después al operador de comparación > (gt = greater than, "más grande que", en inglés).
Desde un punto de vista meramente ortográfico y en lo que atañe a su apariencia, los métodos especiales se distinguen fácilmente porque emplean ese doble carácter underscore (doble guión bajo), tanto al principio como al final del nombre del método, antes de los paréntesis, en este último caso.








      RESUMIENDO, TODOS LOS OBJETOS INSTANCIADOS A PARTIR DE UNA CLASE MODELIZADA POR NOSOTROS, NORMALMENTE, CUENTAN CON UNO O MÁS MÉTODOS QUE INSTANCIAMOS EN EL CUERPO DE NUESTRA CLASE (v. SUPUESTO1). PERO COMO TODAS LAS CLASES QUE MODELIZAMOS HEREDAN A SU VEZ DE LA SUPERCLASE object DE PYTHON, CUALQUIER OBJETO INSTANCIADO A PARTIR DE ELLA, HEREDARÁ A SU VEZ Y POR DEFECTO LOS MÉTODOS DE LA CLASE object TAN LINDAMENTE.
ESTOS MÉTODOS SE CONOCEN COMO MÉTODOS ESPECIALES Y QUE, GRÁFICAMENTE, SE REPRESENTAN CON DOBLE GUIÓN BAJO TANTO DELANTE COMO DETRÁS DEL NOMBRE DEL MÉTODO, Y FINALIZA CON LOS CONSABIDOS PARÉNTESIS QUE ENVUELVEN LOS POSIBLES PARÁMETROS QUE PUEDAN LLEVAR: __nombre del método__()
EVIDENTEMENTE, LA SUPERCLASE object NO TIENE PROPIEDADES O ATRIBUTOS PROPIOS HEREDABLES PORQUE SERÍAN CASI INFINITOS (COLOR, TAMAÑO, ALTURA, ETC.), PERO PUEDE TENER MÉTODOS EN LA MEDIDA EN QUE ESTOS SON GENÉRICOS Y UNIVERSALMENTE (O, POR LO MENOS, AMPLIAMENTE) APLICABLES. POR EJEMPLO, EL MÉTODO ESPECIAL __repr__(), DADO QUE TODO RESULTADO DEVUELTO POR CUALQUIER MÉTODO SOBRE CUALQUIER OBJETO INSTANCIADO EN CUALQUIER CLASE PUEDE SER REPRESENTADO (to represent, "representar", en inglés) COMO UNA string, COMO UNA CADENA DE TEXTO, E IMPRESO (REPRESENTADO) COMO TAL EN PANTALLA. VEMOS UN PAR DE EJEMPLOS A CONTINUACIÓN QUE HE HECHO YO MISMO CON MIS PEZUÑAS.


Y ahora como función integrada de Python: repr().



BARRANCOS DE MASCA CON EL ATLÁNTICO AL FONDO, MACIZO DE TENO, NOROESTE DE TENERIFE.


Como veremos a continuación, la mayoría de estos métodos especiales actúan en segundo plano "detrás" de ciertas funciones nativas o predefinidas y de sentencias como del cada vez que las utilizamos en nuestros códigos. Así Python nos ofrece la oportunidad de utilizar las funciones predefinidas y sentencias homónimas (comparten el mismo nombre, ya lo veremos en una tabla que mostraremos al final del apartado) dentro de nuestras propios métodos formando parte de su bloque de código, definidos por nosotros mismos dentro de una clase, o bien, directamente, como métodos (métodos especiales) si lo que queremos es facilitar de algún modo su llamada posterior desde cualquier ámbito del cuerpo de la clase, estableciendo así una suerte de analogía, de similitud, de parecido salvando las distancias, entre lo que sería una variable local y una variable global.

MÉTODOS ESPECIALES

      1.- __init__():

      
      Es el método especial más común, el más utilizado, hasta cierto punto, el más importante. Tanto es así que si nos fijamos en los ejemplos en los que hemos modelizado clases en estos capítulos dedicados a la POO, es el único que hemos implementado de manera explícita, con lo cual podemos hablar de él como de un "viejo conocido".
Como ya sabemos (v. CLASES I, y siguientes) y sin entrar demasiado en detalles porque ya lo hemos explicado en capítulos anteriores, es el de inicializar objetos (como si dijéramos, "proporcionarles características y capacidades", qué tiene y qué es capaz de hacer, así, de entrada, nuestra clase recién alumbrada y que luego podemos extender a los objetos que instanciemos a partir de ella) cuando instanciamos una clase. Habida cuenta de que inicializamos un objeto a partir de otro, debemos referenciar a ese objeto "padre" con la autorreferencia self, (tal objeto instanciado de tal clase hereda estas características (atributos/propiedades) de esa misma clase) que pasa a convertirse en su primer parámetro formal y obligatorio. A partir de aquí, podemos añadir al método especial todos los parámetros (que luego pasaremos a argumentos en el momento de concretarlos) si lo estimamos necesario.
El método especial __init__(), a parte de inicializar una clase recién modelizada, que es para lo que está, vaya esto por delante y, recordemos, aunque nosotros no la llamemos específicamente, Python la implementa de manera automática, en segundo plano, en las entrañas binarias del programa, nos va a permitir configurar un conjunto de atributos/propiedades que serán asignadas (serán extendidas, las heredarán) a cualquier objeto que instanciemos a partir de ella. No es obligatorio hacerlo, pues podemos perfectamente instanciar métodos que construyan por sí mismos estas mismas propiedades.
Por otra parte, la autorreferencia self nos permitirá llamar o invocar posteriormente en otra clase hija, o dentro de la propia clase.
Veamos una secuencia del proceso.











También podemos insertar en __init__() los parámetros con sus valores correspondientes con el concurso necesario del operador de asignación =, como sabemos de sobra. Posteriormente, cuando instanciemos un objeto, no será necesario pasarle argumentos a la clase: cerramos el paréntesis en vacío y santas pascuas.




Pero si queremos pasar igualmente los parámetros con sus valores correspondientes, tampoco pasa nada, oiga:



El hecho de que pasemos valores concretos en __init__() no implica que estos sean inmutables, que no se puedan modificar, . Por el contrario, podemos cambiar los datos cuando los pasemos en la clase. En este caso es necesario volver a pasar los datos cuando instanciemos al objeto. No es necesario que pasemos todos los argumentos: podemos pasar a argumentos tan sólo aquéllos parámetros (argumentos opcionales, recordemos, como pares clave/valor) que modifican su valor original tal cual se los proporcionamos en __init__().




Sin embargo, si no pasamos ningún parámetro en __init__() no podremos pasarlo luego como si tal cosa, de "contrabando", en el cuerpo del método especial porque Python no los reconocería lanzando una excepción de tipo NameError:



Pero aún así, hay un modo: no pasamos el dato específico pero el tipo de dato que espera. El inconveniente está en que tendremos que concretar el dato, siempre de acuerdo al tipo de dato que hayamos especificado previamente en la llamada al método. Vamos a verlo:


Como podemos observar, posible sí que es, pero práctico, lo que se dice práctico,...
Incluso, cómo no , Python nos permite combinar distintas posibilidades:


En muchas ocasiones, no necesitamos realmente instanciar ninguna propiedad o atributo en el método especial __init__(). De hecho, es muy habitual que los programadores (un servidor lo hace con frecuencia) simplemente implementen el método especial y como cuerpo del mismo pasen únicamente la palabra clave (keyword) pass, para continuar codificando sin mayor incidencia. Así, las propiedades o atributos del objeto se irán instanciando dentro de los diferentes métodos de la clase según los vayamos necesitando, sobre todo, cuando no sabemos de antemano cuántas propiedades vamos a tener que instanciar y/o suponemos que van a ser muchas porque así nuestro proyecto nos lo va a demandar. Por esta misma razón, es aconsejable y hasta recomendable, que si por el contrario, sabemos de antemano (planificación) qué propiedades vamos a instanciar o suponemos que van a ser unas pocas, hacerlo, esta vez sí, dentro del cuerpo del método especial __init__().

COSTA DE LA PUNTA DEL HIDALGO, MACIZO DE ANAGA, NORESTE DE TENERIFE, CON LA SILUETA DEL TEIDE AL FONDO.


      2.- __new__():


      Este nuevo método especial (recordemos que en CLASES I, igual que con el método especial __init__(), lo estudiamos aquí y siguientes con cierta profundidad) "crea", "alumbra", "proporciona carta de vida",  al objeto, a cualquier objeto  realmente, desde lo más profundo de las entrañas de Python, cada vez que lo instaciemos desde alguna clase, la hayamos modelizado nosotros mismos, como por ejemplo en el caso de SUPUESTO I, o de cualquiera de los ejemplos que hemos mostrado en este apartado dedicado a la Programación Orientada a Objetos, como a cualquier otro que instanciemos a partir de una clase ya preconstruida, bien para los elementos propios del lenguaje de Python, como para clases creadas en el sinnúmero de bibliotecas, propias y de terceros, que pueden emplearse en este lenguaje de programación.
Vemos un ejemplo de lo que queremos decir a continuación:



Nuestro método especial se llama así, __new__(), porque como hemos mencionado, "crea" un objeto "nuevo", (new, en inglés) y al que luego el método especial __init__(), que acabamos de estudiar más arriba, lo "inicializa" (to initiate, "iniciar", en castellano) preparándolo para instanciar a partir de él cuantos atributos/propiedades y/o métodos consideremos oportunos dese la propia clase. Con lo que hemos dicho, ya sabemos que cuando instanciamos un objeto a partir de una clase dada, primero y de manera interna se dispara un método especial __new__() que "crea", "alumbra",  "proporciona carta de vida",  al objeto, y a continuación, también de manera interna aunque aquí los programadores suelen ponerlo de manifiesto para instanciar precisamente las propiedades que consideren necesarias (los métodos se suelen instanciar a partir de funciones definidas por el usuario, como ya sabemos).
Primero debe invocarse al método especial __new__() que es quien realmente instancia (porque "crea") al nuevo objeto, y después se llama  al método especial __init__() para inicializarlo posteriormente con sus atributos/propiedades originales, de partida, por así decirlo, y "ponerlo en funcionamiento" aunque, en la práctica, nos basta con implementar sólo el método especial de  inicialización __init__(), ya que si nosotros no proporcionamos el método especial __new__(), Python lo hará por su cuenta de manera automática.



Un gran acierto de la POO consiste en no tener que volver a implementar un método en ninguna subclase que podamos modelizar a continuación en sucesivos bloques de código a lo largo de nuestro programa y que ya estudiaremos en su momento, si nos es suficiente con el método de la clase base, clase padre o superclase, cualquiera de los tres nombres nos sirven, para no repetirlos (reutilización, reusabilidad, los mantras propios de la POO), vamos: si invocamos a un método en un objeto (en consecuencia, previamente instanciado, claro), y en la clase a la que pertenece (desde donde lo hemos instanciado, vaya) no hemos instanciado ese mismo método que acabamos de llamar, el bueno de Python revisará de manera predeterminada las clases base, clases padre o superclases, que hayamos podido modelizar previamente en nuestro código (es decir, se REMONTARÁ en la jerarquía de clases leyendo de abajo a arriba, "desandando" el camino pergeñado por el flujo de ejecución de un nivel a otro), escalando hasta la propia clase base object si fuera menester, hasta encontrar al dichoso método. En caso de no encontrarlo por más que el bueno de Python se haya esforzado en buscarlo lanzará una excepción del tipo AtributteError como un suspiro binario de rendición.
Al contrario que con __init__(), el método especial __new__() no requiere de la autorreferencia self (¿para qué, si es él mismo quien "crea" la criatura pero no quien lo inicializa, quien "le da forma"?. Por eso, y a  partir de la ejecución de __new__() donde se crea el objeto, todos los demás métodos especiales, incluido __init__(), que podamos invocar desde la superclase object, y el resto de métodos que instanciemos a partir de las funciones definidas por el usuario, deberán llevar como argumento primero y obligatorio la autorreferencia self.
Sí recibe este método especial __new__(), por el contrario, una autorreferencia a la clase donde se encuentra (cls, por convención) también como argumento único (único porque la sola misión que tiene en la vida es "crear", instanciar, vamos, el nuevo objeto y se acabó) obligatorio. Como veremos al final, si aún así insistimos en pasarle algún parámetro (¿¡¡¡¡para qué!!!!?), esos mismos parámetros, obviamente, tendremos que reproducirlos en el método especial inicializador __init__(). Doble trabajo.
Con lo dicho podemos deducir que nuestro método especial constructor __new__() es, en realidad, una suerte de  método estático (v. MÉTODOS DE CLASE, ESTÁTICOS Y @PROPERTY) mondo y lirondo que siempre devolverá el último objeto que haya creado.
Bueno, y nos podemos preguntar: si en la práctica rara vez se utiliza __new__(), ¿para qué está disponible? Básicamente, para facilitar la personalización, en aras de lograr el mayor control posible a la hora de modelizar nuestras clases, habilitar cierto tipo de patrones (patterns, en inglés, y que veremos en un capítulo posterior), y favorecer la distinción  entre la modelización de clases que heredan (el concepto de herencia será tratado más adelante) de aquéllas otras que son "inmutables", como las clases de strings y tuplas (class str y class tuple).
Por otra parte, el método especial constructor __new__(), que preexiste al método especial inicializador __init__(), donde el primero "crea", "construye" al muñeco y el segundo le "pone las pilas" y, por tanto, así deben estar jerarquizados, sólo invocará a __init__() siempre y cuando  la ejecución de __new__() devuelva un objeto, una instancia.




     UN APUNTE IMPORTANTE: A PESAR DE LO QUE HEMOS DICHO CON ANTERIORIDAD,  SI NOS EMPEÑAMOS EN INTRODUCIR PARÁMETROS ADICIONALES EN EL MÉTODO ESPECIAL CONSTRUCTOR __new__(), JUNTO A LA REFERENCIA cls A LA CLASE, ESTOS MISMOS DEBEN IR TAMBIÉN EN LA ZONA DE PARÁMETROS DEL MÉTODO ESPECIAL INICIALIZADOR __init__(). LO VEMOS EN EL EJEMPLO QUE SE MUESTRA A CONTINUACIÓN.



FLORACIÓN EN PRIMAVERA DEL TAJINASTE ROJO, FLORA ENDÉMICA Y EXCLUSIVA DE LAS CAÑADAS DEL TEIDE, CON EL VOLCÁN AL FONDO, A LA DERECHA, CON LA VÍA LÁCTEA DE FONDO, DURANTE UNA PRECIOSA NOCHE ESTRELLADA. CENTRO DE TENERIFE.

      3.- __repr__():


      Existe un método o función predeterminada en Python, repr(), por el cual podemos crear una representación de un objeto que hayamos instanciado previamente, una especie de copia o modelo del objeto original, con sus mismos atributos y valores. Si el objeto del que queremos construir una representación es una string (cadena), podemos recrearla perfectamente tan sólo a partir de su valor.


Esta representación, valga la redundancia, representa al objeto que se le pase como argumento en modo string (cadena). Por eso, si prestamos atención al primer ejemplo de la captura anterior, vemos que el resultado de representar un dato o valor (a = "HOLA"repr(a)), es ¡una doble string! Observemos: "'HOLA'": Envolvemos en una string otra string. Es lo que tiene.
Si lo que queremos representar, en cambio, son clases  previamente modelizadas por nosotros mismos, no nos queda otro remedio que acudir al método especial __repr__ (). Cuando queramos representar una clase, debemos incluir sus atributos y valores correspondientes.


Modelizamos la clase atomo (recordemos que los nombres que proporcionemos a las clases deben escribirse, por convención, en mayúsculas, por lo que un servidor debe entonar el mea culpa, y no es la primera vez que sucede ni tampoco, a buen seguro, será la última) y en su declaración de inicio con el método especial __init__(), junto a la autorreferencia self, insertamos, pasamos, tres argumentos (1.): elemento (variable que almacena el nombre del elemento químico), su numero atómico a través de la variable numero_atomico, y su masa atómica, a través de la variable masa_atomica.
Notemos que aunque en nuestro ejemplo los valores de los atributos elementonumero_atomico, y masa_atomica son strings, podemos pasar sin ningún problema otro tipo de dato, como un entero o un decimal, por ejemplo: el método especial __repr__() lo representará igualmente como una string, como una cadena. Veremos al final un ejemplo para este caso.
En 2. instanciamos las propiedades/atributos asociados al objeto.
En 3. invocamos al método especial que tan sólo lleva, en 4., como bloque de código, una sentencia return con un tipo de formato de salida (mediante el método str.format() de las cadenas que más nos haga tilín.
En 5. vemos como resolver la cuestión en caso de contar con variables que no almacenan strings como valores.
En el ejemplo podemos ver cómo hacemos para instanciar los objetos que queramos, y aprovechar el método especial __repr__() para aplicarles a éstos los mismos procedimientos que hemos aplicado en el original.
Veamos para finalizar, un ejemplo con un tipo de dato que no es una string:


     
CAE LA NOCHE SOBRE LA ACCIDENTADA COSTA NORTE DE ANAGA, MACIZO DE ANAGA, NORESTE DE TENERIFE.



      4.- __str__():


      Con estas tres letras entre doble guión bajo y doble guión bajo, no hay que ser muy listo para intuir por dónde van los tiros con este método especial. Este método especial se sustancia cuando invocamos a las funciones str(), conversora, y print(), "impresora". Guarda bastante similitud con el método especial __repr__() y, como él, devuelve una string, una cadena de texto, aunque en su caso, el método especial __repr__() representa pero no convierte (hace una simulación, para entendernos) mientras que el método especial __str__() que ejecuta una conversión real, como lo hace la función integrada str().





       ESTE MÉTODO __str__() RESULTA UN MÉTODO ÚTIL PARA DEPURAR NUESTRO CÓDIGO.



      5.- __bytes__():


      Similar al método especial anterior sólo que se aplica cuando invocamos a la función predefinida bytes(). Devuelve una reproducción del objeto como tipo de dato binario.




      6.- __format__():


      Obtenemos con este método especial una representación del objeto como cadena de texto en un formato de salida (output) determinado.

       7.-__hash__():


       Este método especial se ejecuta cuando llamamos a la función integrada hash(), que nos devuelve un número entero, lo que nos permite efectuar una comparación usando el entero como identificador, si tenemos dos objetos iguales en tanto que de ser así, los identificadores serían los mismos, ergo, ambos objetos son el mismo objeto.
Si comparamos dos instancias (dos objetos, vamos), éstas serán siempre potencialmente diferentes a menos que se comparen entre sí y, con el método especial, se determine si son iguales o no.



      8.- __bool__():


      Este método especial se ejecuta cuando llamamos a la función integrada bool(), y recibe como argumento la instancia desde una clase concreta. El valor por defecto cuando no implementamos el método especial en el bloque de código de nuestra clase es True.
En el caso de que la implementemos nos devolverá, como es de suponer, un valor, un tipo de dato booleano: True o False.



      8.- __del__():


    Este nuevo método especial actúa sobre aquellos objetos que van a ser destruidos. En Python contamos con un elemento particular que ejerce de "recolector de basura", en inglés, garbage collector, que invoca al método especial __del__() a la vez que libera el espacio de memoria que ocupaba el objeto en cuestión en nuestro disco duro cuando éste es destruido.
Normalmente, el método especial método especial __del__() no se llama de manera explícita: en la práctica, no se hace nunca. Lo único que hace la sentencia del es eliminar una referencia decrementándola unidad por unidad: si contamos con 5 referencias de objeto que apuntan al objeto almacenado en memoria y ejecutamos la sentencia del nombre_variable, sólo se elimina una referencia mientras que se mantienen las otras 4, es decir, elimina la referencia en memoria que encuentre primero en su búsqueda que apunta al objeto pero no al objeto mismo, a menos que tengamos una única referencia, una sóla, por lo que, a parte de eliminar la propia referencia eliminará al objeto mismo.





Sólo cuando han sido eliminadas todas la referencias al objeto (y, por consiguiente, el objeto queda almacenado en memoria aunque ya no podremos acceder a él a la espera de ser sobreescrito por un objeto nuevo) actúa el recolector de basura que mencionábamos al principio, que llama, por su propia cuenta y riesgo, ahora , al método especial __del__(), y DESTRUYE al objeto, liberando su espacio de memoria para ser recuperado por nosotros cuando creemos un objeto nuevo y el bueno de  Python decida colocarlo allí, en ése mismo espacio de memoria tan cómodo y tan chic, que ocupaba el objeto que destruyó en su momento.
Por esta razón, __del__() siempre actúa al final y, por el mismo motivo, no tiene sentido que lo invoquemos nosotros mismos: ya lo hace el garbage collector él solito cuando toca hacerlo.
Resumiendo: es mejor no implementar al método especial __del__() en nuestras clases (a pesar de estar disponible) Dejemos que el intérprete de Python invoque al garbage collector cuando lo estime oportuno evitándonos la pérdida no deseada de datos.
Para saber si un objeto ha sido eliminado convenientemente, contamos con dos procedimientos: el bloque try... finally (TRATAMIENTO DE EXCEPCIONES) , por una parte, o el recurso a un objeto de contexto asociado a una sentencia with (WITH).



MÉTODOS ESPECIALES DE COMPARACIÓN


      Podemos comparar objetos instanciados desde nuestras clases modelizadas a través de los métodos especiales de comparación.


Para hacer uso de ellos tan sólo tendremos que escribir el método especial que necesitamos en el cuerpo de nuestra clase.
Los métodos especiales de comparación se invocan de acuerdo al operador que empleamos en la comparación: si usamos <=, se invocará al método especial __le__(); y si usamos el operador != se invocará al método especial __ne__()
Terminamos este apartado con un ejemplo sencillito que incluye un método especial y un método especial de comparación:







      SI NOS HEMOS FIJADO, EN TODOS Y EN CADA UNO DE LOS MÉTODOS ESPECIALES QUE HEMOS TRAÍDO AQUÍ, NO NECESITAMOS INVOCAR A LA CLASE (SUPERCLASE) object PARA QUE, AMABLEMENTE, NOS "CEDIERA" SUS PROPIOS MÉTODOS (MÉTODOS ESPECIALES) PARA PODER USARLOS NOSOTROS MISMOS A CONVENIENCIA EN NUESTROS CÓDIGOS. ¿Y POR QUÉ ESTO ES ASÍ? PUES PORQUE "NUESTRA RECTORA", CONTINUANDO CON LA ANALOGÍA DEL COMIENZO  NOS FACULTA SU USO SIN NECESIDAD DE PEDIRLE PERMISO: PARA NO INCURRIR EN REITERACIONES INNECESARIAS CADA VEZ QUE MODELICEMOS UNA CLASE Y EN SU ZONA DE PARÁMETROS TENGAMOS QUE PONER SIEMPRE EL NOMBRE DE LA SUPERCLASE (object): ADEMÁS, DOS DE SUS MÉTODOS ESPECIALES VAN A ESTAR SIEMPRE PRESENTES DE UNA MANERA U OTRA: __new__() E __init__(). POR OTRA PARTE, PROCEDER DE ESTA MANERA (ELIPSANDO object) NOS FACILITA EL RECURSO A LOS MÉTODOS ESPECIALES EN CUALQUIER PARTE DEL CUERPO DE NUESTRA CLASE SIN MAYORES COMPLICACIONES. FINALMENTE, Y AL MARGEN DE OTRAS CONSIDERACIONES MENORES, EVITA LA "SOBREPOBLACIÓN" DE LA ZONA DE HERENCIAS, POR ASÍ LLAMARLA, (QUE ES EL EQUIVALENTE EN LAS CLASES A LA ZONA DE PARÁMETROS DE LOS MÉTODOS), Y QUE VEREMOS MÁS ADELANTE EN LOS APARTADOS QUE SIGUEN. DE TODAS FORMAS, SI NOS HACE ILUSIÓN ESCRIBIR EL NOMBRE DE LA SUPERCLASE, PODEMOS HACERLO TRANQUILAMENTE, COMO YA MOSTRAMOS EN ALGÚN EJEMPLO ANTERIOR EN LOS CAPÍTULOS DEDICADOS A LAS CLASES. VEAMOS, AÚN ASÍ, UN PEQUEÑO EJEMPLO PARA RECORDARLO. ¡UY, SE ME HA SECADO LA BOQUITA CON TANTO LADRIDO! NADA, ME VOY A BEBER AGÜITA... 




AMANECE SOBRE EL PARQUE NACIONAL DE LAS CAÑADAS DEL TEIDE, CON LAS PRIMERAS LUCES DORADAS DE LA MAÑANA Y EL CELESTE CLARO DEL MAR DE NUBES REMONTANDO DESDE EL PINAR. CENTRO DE TENERIFE.

4 comentarios:

  1. De nuevo un trabajo increible. Estoy aprendiendo Python y gracias a sus historias y a sus ejemplos es un placer leer y entender este hermoso lenguaje a manos de usted. Parece un cuento narrado donde todo cobra sentido. Esta lección ha sido genial.
    Siga así.
    Carlos

    ResponderEliminar
  2. ¿Qué puedo decirle D. Carlos? GRACIAS infinitas por sus palabras. Con experiencias como la suya donde la humilde aportación de quien escribe le procura el placer del aprendizaje y, aún más, la dicha de disfrutar haciéndolo, créame, da por bueno todo el trabajo que está detrás de este blog y hasta en lo personal, me justifica la vida misma. Un placer. Saludos.

    ResponderEliminar
  3. Excelente blog. Saludos y felicitaciones para el autor!!

    ResponderEliminar
  4. Muchísimas gracias por sus palabras. Como siempre, un placer resultar útil. Saludos y buen aprendizaje.

    ResponderEliminar