CLASES II

CHIMENEAS DE HADAS. FORMACIONES DE PIROCLASTOS Y PUMITAS FRUTO DE LA EROSIÓN. ENTORNO GEOMÓRFICO DEL PAISAJE LUNAR, DENTRO DEL PARQUE NACIONAL DE LAS CAÑADAS DEL TEIDE DESDE SU FLANCO SUR, TÉRMINO MUNICIPAL DE GRANADILLA DE ABONA, SUR DE TENERIFE.

      Hemos visto cómo podemos instanciar propiedades directamente desde el método especial de Python __init__(self). Pero no necesariamente tenemos por qué darles un valor sobre la marcha: igual sólo nos interesa instanciar la propiedad y asignarle mas adelante el valor que queramos. Como ya sabemos que podemos pasar argumentos al inicializador, podemos colocar aquí los nombres de las propiedades (que, como argumentos obligatorios que son, serán variables), inicializarlas con self para que se le atribuyan a cualquier objeto que se instancie desde la clase, y posteriormente, cuando lo instanciemos, aprovechar y proporcionarle los valores que queramos a esas variables/propiedades.
Veámoslo con un ejemplo:









Como ya hemos dicho, es posible modelizar una clase en Python con varias "variables e instancia" pasadas como argumentos.
Veámoslo con un ejemplo sencillo:



En este ejemplo hemos prescindido de la función inicializadora __init__(self), para consignar que su inclusión en una clase no es imprescindible para que ésta funcione ya que en última instancia Python la incorpora de manera automática (aunque sí absolutamente recomendable en un código que se ajuste al canon, sobre todo, si el código de la clase que hemos modelizado resulta algo extenso), y hemos introducido un parámetro más en la zona de parámetros de las dos funciones declaradas en su interior, obviamente, a continuación de la obligada autorreferencia self, pot_cuadrado y pot_cubo y, además, hemos optado por asignarle un algoritmo simple en lugar de un valor tal y como hemos venido haciendo hasta ahora.
Así pues, de manera sibilina, como quien no quiere la cosa 😉, hemos insertado lo que se llama argumentos por defecto: el argumento obligatorio  x. Seguro que nos sonará de cuando "peleamos" con las funciones definidas por el usuario (def) en este mismo manual, Un argumentos por defecto es aquél que proporciona directamente el programador en previsión de que el usuario no lo proporcione y permitir así que corra el flujo de ejecución del programa sin que Python lance una excepción.
Como vemos, el código corre divinamente y arroja los resultados esperados. No parece, pues, que sea muy complicado, ¿verdad? Vamos a hacer lo propio pero introduciendo esta vez más argumentos:



En este ejemplo, el método caracteristicas que remeda, sí, al inicializador __init__(self), incorpora en su zona de parámetros, siempre a continuación de self, una serie de argumentos obligatorios: marca, modelo, color y caballaje. En el cuerpo de la función es donde convertimos estos cuatro parámetros en sendas variables de instancia, por mediación del autorreferente self y la sintaxis de punto sobre cada uno de los parámetros (self.marca, self.modelo, self.color, self.caballaje), con lo que le decimos al intérprete de Python que los valores que se asignen a estas variables se aplicarán a todos, absolutamente, todos los objetos instanciados a partir de la clase caracteristicas.
Podemos definir como un método de instancia (caracteristicas, en el ejemplo), que tal es su denominación técnica, como una función que se define (def) dentro del cuerpo de una clase (class), y cuyo primer parámetro (self) es una referencia de mención obligada que apunta al objeto que se instancia a partir de la clase (en el ejemplo superior, a la class coche). Basándonos en esta definición, el método o función inicializadora __init__(self) es un método de instancia, con sus peculiaridades, sí, pero un método de instancia mondo y lirondo; pero, ¡ojo!,  el método o función constructora __new__(cls), no, evidentemente, porque tal y como indica su parámetro obligatorio: cls, que apunta a la propia clase y no self, es la encargada de CONSTRUIR la clase y sólo a partir de una clase ya construida se pueden instanciar propiedades, métodos y objetos. Cómo esto va a suceder SIEMPRE así cada vez que modelicemos una clase, Python recurre a la elipsis (ellipsis) y lo hace él solito por nosotros.
Agradecidos, oiga.
Vemos cómo instanciamos un objeto (toyota_yaris) a partir de la class coche, con el operador = de asignación y con la sintaxis que ya conocemos: toyota_yaris = coche(). En la línea siguiente, por mediación de la sintaxis del punto (dott method) llamamos al método caracteristicas para aplicarlo sobre el objeto o instancia toyota_yaris y, mediante el consabido paso de parámetros a argumentos, proporcionamos valores a nuestras variables de instancia: marcamodelocolor y caballaje: "TOYOTA", "YARIS", "GRIS CENIZA", "95 hp", con lo que a partir de ahora, nuestro objeto o instancia toyota_yaris tendrá por los siglos de los siglos (y siempre y cuando no modifiquemos los valores) estas características que acabamos de citar.
Si por mediación de nuevo de la sintaxis del punto llamamos a cualquiera de las variables de instancia sobre nuestro objeto toyota_yaris, nos mostrará su valor: toyota_yaris.color, nos devuelve GRIS CENIZA.
Proponemos ahora un nuevo ejemplo de modelización de una clase, instanciación de objetos, propiedades y métodos extraído de los videotutoriales del profesor Jesús Conde (recomendamos encarecidamente la suscripción a su canal).



TAJINASTE ROJO EN FLORACIÓN. PLANTA ENDÉMICA DE LAS ALTURAS DE TENERIFE, DURANTE LA NOCHE. PARQUE NACIONAL DE LAS CAÑADAS DE EL TEIDE. TENERIFE.

Podemos hacer una sencilla traza del programa: en la primera línea, importamos el módulo math de la biblioteca estándar de Python (Python stdlib) manteniéndolo en stand by, en espera, hasta el momento de utilizarlo. En la tercera línea de código , se modeliza una clase (class) con el classname Punto (recordemos que deben comenzar en mayúscula, aún cuando a un servidor, con las prisas, se le olvide. Tenedlo en cuenta, por favor). Como finalizamos con los dos puntos preceptivos el intérprete sabe que a continuación viene un bloque de código  que se codificará en el ámbito (scope) de la clase Punto y, en consecuencia, se nos exige un indentado o sangrado.
En la cuarta línea, el flujo de ejecución se encuentra con con la palabra reservada (keyword) def que le advierte de que se va a crear una función definida por el usuario (y, por consiguiente no built-in o nativa de Python) dentro del ámbito de la clase, como acabamos de decir, por lo que, como tal, pasa a denominarse método. Dicho método llevará en el espacio de nombres (namespace) el referente mover por el que se identificará al método. Si nos acordamos, nos daremos cuenta que  tanto en la introducción a la Programación Orientada a Objetos como en Clases I el presente manual, señalábamos que los métodos apuntaban a acciones que podían reflejarse bien como infinitivos de verbo. Como vamos a crear el método mover dentro del ámbito de la clase Punto debemos colocar el autorreferente obligatorio self como primer argumento de su correspondiente zona de parámetros. A continuación de self, encuentra dos parámetros más del método: x e y, convenientemente separados por comas. Se cierra el paréntesis que delimita la zona de parámetros y termina la línea con dos puntos, señalando un nuevo ámbito de codificación, en este caso, el que corresponde al método mover, mediante el necesario sangrado.
Aquí se lleva a efecto la conversión de los parámetros x e y a variables de instancia mediante la autorreferencia self, la sintaxis de punto y el nombre de los parámetros según los proporcionamos en la zona de parámetros del método. Mediante el operador = de asignación, a estas variables de instancia se les asigna un nombre y ya podemos decir que están perfectamente instanciadas y listas para aplicarse sobre cualquier objeto que instanciemos a su vez de la clase Punto: self.x = x y self.y = y. Nótese que no hemos empleado ningún pie de función (bien return, o bien print()), dado que no nos interesa (ni tiene tampoco sentido, la verdad) que nos devuelva o muestre un resultado sino tan solo declarar las variables de instancia como Dios manda.


Pasamos a la séptima línea, y el flujo de ejecución se encuentra con una nueva declaración def, con el espacio de nombres reiniciar y una zona de parámetros que sólo contiene el argumento obligatorio self. Para la siguiente línea, instanciamos una propiedad a través de self, el nombre del método que acabamos de crear, mover, y su correspondiente zona de parámetros con sus parámetros respectivos pasados ya como argumentos: 0 y 0, es decir, x = 0 e y = 0. Con esto tenemos que le hemos asignado valores a las variables de instancia x e y. Constituye un buen ejemplo de reutilización o reusabilidad del código a pequeña escala: como la idea de reiniciar la posición de un punto consiste en desplazarlo hasta un punto de inicio con las coordenadas x = 0 e y = 0, aprovechamos el método previo que acabamos de construir, mover,  y le damos precisamente esos mismos valores, 0 para la x y 0 para la y: (0, 0), en la zona de parámetros de mover.
En la novena línea del código, el flujo de ejecución se encuentra con una nueva declaración def, su espacio de nombres correspondiente, esta vez, calcular_distancia, y su correspondiente zona de parámetros, con el autorreferente obligatorio self como primer argumento, al que le sigue otro parámetro, también obligatorio, otro_punto. Cierre de paréntesis y dos puntos.
En este caso, en el ámbito del método introducimos directamente una declaración return para que devuelva la ejecución de una expresión matemática: aquí llamamos a la clase sqrt del módulo math (acabamos de invocarlo por lo que deja de estar en modo stand by y pasa a estar activo) por la sintaxis del punto y que nos permite trabajar con raíces cuadradas. Hallará, pues, la raíz cuadrada del resultado de la siguiente expresión: la suma de self.x - otro_punto.x ** 2 self.y - otro_punto.y ** 2. El Teorema de Pitágoras, ni más ni menos.
Pasamos ahora a la instanciación de sendos objetos punto1 y punto2 en las líneas 15 y 16 respectivamente de acuerdo a la sintaxis específica para el caso: punto1, operador de asignación =, y nombre de la clase a la que pertenecen los objetos que acabamos de referenciar con un paréntesis al final del nombre: Punto(). Lo mismo exactamente para el objeto punto2.
El flujo de lectura "toma nota", no encuentra nada de particular, prosigue su trayecto descendente y alcanza la línea 18. Aquí se encuentra una referencia al objeto punto1 del que, por mediación de la sintaxis del punto, se requiere una llamada al método reiniciar(). El intérprete de Python remonta el código con el mismo arte y destreza que un escalador profesional y se ubica en la línea 7, que es donde se encuentra el método del mismo nombre (gracias al espacio de nombres, namespace, sabe perfectamente qué y dónde buscar).
Como ya sabe que pertenece a la misma class Punto, dado que no lanzó excepción alguna en el momento de instanciarse el método en la antedicha línea 7, y como el método mover() al que se llama desde el propio método reiniciar() es TAMBIÉN una instancia de la class Punto, en tanto cumplen ambos con los requisitos sintácticos necesarios, y el objeto punto1 instanciado en la línea 15 sobre el que llamamos al método es TAMBIÉN una instancia de la misma class Punto, no encuentra incompatibilidades, determina que ambos son "hijos" de la misma clase y no advierte inconveniente alguno en proporcionarle a nuestra instancia punto1 el método reiniciar(), que hará que nuestro punto1 tenga el valor que le proporciona el método mover() al que se llama a su vez desde el método reiniciar() que, en el paso de parámetros hemos determinado que sea 0, 0 (línea 8). Ergo, nuestro punto1 tiene por coordenadas los valores 0, 0 (x = 0 e y = 0).
Salta a continuación nuestro intrépido flujo de lectura a la línea 19 y detecta que se invoca al método mover() para el objeto punto2. El intérprete de Python de nuevo remonta hasta la cuarta línea del código dado que es aquí donde encuentra la referencia al método (el namespace de marras), procediendo en consecuencia, a aplicar su bloque de código correspondiente. Observa que dicho  método precisa de de dos parámetros, x e y. El intérprete desciende de nuevo a la línea 19 para comprobar si, en efecto, se aportan valores (argumentos) para los mencionados parámetros x e y y llevar a cabo un "paso de parámetros", esto es, convertir un parámetro en argumento, pasar de lo "abstracto" a lo "concreto", como Dios manda. En tanto que encuentra sendos valores válidos para efectuar la transición y de acuerdo a la ley de correlación donde al primer parámetro de la zona de parámetros le corresponde el primer argumento, al segundo parámetro de la zona de parámetros le corresponde el segundo, al tercer parámetro el tercer argumento, y así sucesivamente, halla dos datos de tipo int, 5 que asignará a la coordenada x0 que asignará a la coordenada y, procediendo pues a la conversión.
Como ya no queda más por hacer, el flujo de ejecución baja a la línea 20 donde se encuentra con una llamada a nuestra función favorita, print(). ¿Y qué es lo que tiene que imprimir? Pues la aplicación al objeto punto2 del método calcular_distancia(). Nuestro intérprete viajero (y presumimos que ya algo mareado a estas alturas) remonta hacia la línea 9 donde encuentra la referencia que apunta al método en cuestión. A continuación recorre su cabecera (header), que requiere de un argumento para el parámetro otro_punto. Baja como un tiro a la línea 20 y detecta que se proporciona el argumento necesario: punto1, que como ya sabemos, vale 0, 0, como resultado de la ejecución de la línea 18 como descubrimos más arriba. Aplica, pues, el intérprete de Python regresando a la línea 11 el valor de punto1 y calcula el algoritmo: self.x = 5, self.y = 0, de acuerdo a los argumentos proporcionados en la línea 19, y otro_punto.x = 0 y otro_punto.y = 0, de acuerdo a los valores de punto1, que es valor asignado a otro_punto, y que ya sabemos que es 0, 0. Así, sqrt((5 - 0) ** 2 + (0 - 0) ** 2).
Como resultado se obtiene 5 tal y como muestra la línea 21.
Como podemos imaginar, Python nos permite usar variables para hacer más sencillas las llamadas o invocaciones, sin "gastar" tanta sintaxis. Mostramos un ejemplo, práctico:



Cuando usamos nombres de métodos muy largos, la verdad es que esta facilidad para asociar objetos a variables nos ayuda mucho en nuestro trabajo como programadores a la par que nos permite "airear", a despejar el código.

PUESTA DE SOL DETRÁS DE LA MONTAÑA ROJA, PLAYA DE EL MÉDANO, MUNICIPIO DE GRANADILLA DE ABONA, SUR DE TENERIFE.

2 comentarios:

  1. Muchas gracias por todo el apoyo, me ha servido mucho tu blog, la verdad que no sabía lo importante del inglés, ahora que tomo clases en Ringteacher España, he avanzado mucho en mi estudio de programación.

    ResponderEliminar
  2. Hola. Gracias a usted por haber confiado en nosotros para introducirse en este lenguaje de programación. Tiene usted más razón que un santo: aprender inglés es una apuesta segura a caballo ganador y, en lo que al mundo de la informática se refiere, un requisito cai imprescindible. Celebramos con muchísima satisfacción saber que le hemos servido de ayuda. Por favor, si no lo ha hecho, no olvide de echar un vistazo a las entradas, donde se explican muchos más aspectos del lenguaje. Saludos y adelante.

    ResponderEliminar