lunes, 27 de febrero de 2023

Opinión. Documentos de evaluación.

Documentos para la evaluación psicopedagógica -1.

La evaluación psicopedagógica es una de las tarea más complejas que debe realizar un SEO y para los EOE es la que mayor carga de trabajo conlleva. A ello contribuye en gran medida el Programa de Nuevas Escolarizaciones. Por diferentes motivos, la Administración educativa provee de documentos para el desarrollo del proceso.


Esta práctica cubre un triple objetivo:

  • Garantizar que el proceso se desarrolla formalmente conforme a norma.
  • Dar uniformidad procedimental al proceso
  • Y facilitar modelos de referencia a los SEO.
También se pueden identificar aspectos negativos:
  • Representan una interpretación concreta de la norma; en la práctica la se identifica como "correcta".
  • Convierte al/ a la OE en técnico, limitando su autonomía profesional.
  • Los documentos generan una carga de trabajo burocrático no  siempre optimizado.
En estas entradas me voy a detener en lo que implican los puntos terceros de ambas listas, en coherencia con el objetivo de este blog. Su abordaje está sobradamente justificado  en función de la optimización del trabajo de los SEO, algo que entiendo no genera mayor controversia.

La Consejería de Educación del Principado de Asturias proporciona tres modelos documentales: Informe psicopedagógico, Dictamen de escolarización y Acreditación, los cuales están disponibles desde la Intranet de la Consejería. Paso a presentarlos brevemente.


En entradas posteriores me detendré, en primer lugar en el documento Acreditación. En la que sigue a la actual explico los motivos de esta elección.

jueves, 9 de febrero de 2023

Introducción. Modelos de programación.

 POO. Atributos. Modificar valores

Una de las tareas que con más frecuencia debemos realizar al trabajar con objetos es dar valor a los atributos y modificar esos valores. En la entrada anterior en la que comparamos funciones con métodos ya estuvimos trabajando sobre ello, así como en la entrada referida a los atributos. En ésta nos vamos a centrar en esta cuestión específicamente.


Caben tres posibilidades u opciones para modificar el valor de un atributo:
  • Modificarlo directamente
  • Modificarlo mediante un método específico
  • O modificarlo de forma incremental, también mediante un método
Partiremos de una clase simple, en cuyos objetos no se tiene pensado modificar el contenidos de los atributos, para ir mostrando las tres opciones enunciadas antes.

class coche:
    def __init__(self,marca,modelo,anno):
        self.marca=marca
        self.modelo=modelo
        self.anno=anno

    def descripcion(self):
        coche_descripcion= f"Mi coche es un {self.marca} {self.modelo} de {self.anno}"
        return coche_descripcion

mi_coche=coche("Seat","Toledo",1996)
texto=mi_coche.descripcion()
print(texto)

La única novedad que presenta este script es que el método (descripcion(self)) devuelve (return) una cadena f que describe el objeto, por lo que su uso debe asociarse a una variable (texto). Por lo demás, presenta un funcionamiento de sobre conocido.

Para explicar las implicaciones de la modificación de atributo, deberemos incluir en la definición de atributos uno que no figure como parámetro, por ejemplo, el valor del cuentakilómetros, así como un método para mostrar el cuentakilómetros actual del vehículo. Esto modifica la definición de la clase como sigue:

class coche:
    def __init__(self,marca,modelo,anno):
        self.marca=marca
        self.modelo=modelo
        self.anno=anno
        self.cuentakilometros=0 

    def descripcion(self):
        coche_descripcion= f"Mi coche es un {self.marca} {self.modelo} de {self.anno}"
        return coche_descripcion

    def cuentaK(self):
        print(f"El cuentakilómetros de mi coche marca {self.cuentakilometros} kilómetros")

Procedamos ahora a introducir/modificar el valor inicial del parámetro cuentakilometros directamente en la definición del objeto mi_coche:

mi_coche=coche("Seat","Toledo",1996)
mi_coche.cuentakilometros = 1200
texto=mi_coche.descripcion()
print(texto)
mi_coche.cuentaK()

Una vez definido el objeto mi_coche (mi_coche=coche("Seat","Toledo",1996)) establecemos el valor del atributo no definido como parámetro (y en consecuencia no establecido ya como valor en la definición del objeto) mediante la expresión basada en la sintaxis del punto (mi_coche.cuentakilometros = 1200). Posteriormente llamaremos a este método de forma directa (mi_coche.cuentaK()), ya que originalmente no cuenta con la instrucción return.

La segunda opción consiste en modificar el atributo mediante un método específico, el siguiente:

    def cuentaK(self, km_actuales):
        self.cuentakilometros = km_actuales
   print(f"El cuentakilómetros de mi coche marca {self.cuentakilometros} kilómetros")

... que asocia el valor dado al atributo (km_actuales) al parámetro cuentakilometros que después se imprime por pantalla mediante la instrucción print()

mi_coche.cuentaK(1200)

Finalmente veamos cómo se concreta el método que permite incrementar el valor del parámetro, añadiéndolo como método nuevo.

class coche:
    def __init__(self,marca,modelo,anno):
        self.marca=marca
        self.modelo=modelo
        self.anno=anno
        self.cuentakilometros=0

    def descripcion(self):
        coche_descripcion= f"Mi coche es un {self.marca} {self.modelo} de {self.anno}"
        return coche_descripcion

    def cuentaK(self,kilometraje):
        self.cuentakilometros =kilometraje

    def leer_cuentaK(self):
        print(f"Este coche ha recorrido {self.cuentakilometros} Km")

    def incrementarKm(self,km):
        self.cuentakilometros += km

Observa que he eliminado la instrucción print() del método cuentaK() primitivo y he creado un método específico de escritura de los Km que lleva recorridos el coche (leer_cuentaK()). De este modo independizamos ambos procesos (dar valor al atributo cuentakilometros y escribir el resultado. El objetivo es reutilizar este método cuando incrementemos los kilómetros, que es lo que hacemos con el método incrementarKm()

Este es el método que nos permite incrementar los kilómetros inicialmente establecidos (mediante el método cuentaK()). Observa que incrementarKm() tiene dos parámetros: el obligatorio self y km, que serán los km que incrementemos cada vez que usemos el método. La instrucción de este método consiste en añadir km al dato que contiene el atributo cuentakilometros.

Desde el lado de la creación de un objeto (asociado a la variable mi_coche)...

mi_coche=coche("Seat","Toledo",1996)
texto=mi_coche.descripcion()
print(texto)
mi_coche.cuentaK(1200)
mi_coche.leer_cuentaK()
mi_coche.incrementarKm(100)
mi_coche.leer_cuentaK()

... podemos apreciar el uso de los cuatro métodos de la clase, incluyendo el uso repetido del método leer_cuentaK(): primero para informar de los km del cuentakilómetros y después para informar del resultante tras el incremento.

martes, 7 de febrero de 2023

Python. Lenguaje.

Datos alfanuméricos. Líneas complejas

Me ha surgido la necesidad de escribir líneas de código complejas formadas por string también complejos así que me he visto obligado a buscar una solución para crear un código legible a la vez que una salida por consola igualmente legible. 


Dado que esta situación puede darse con cierta frecuencia al manejar textos y variables alfanuméricas de cierta longitud y complejidad, me para que aportar soluciones de este tipo no es perder el tiempo en minucias, así que vamos a ver como podemos resolver este tipo de situaciones.

En primer lugar, cuando escribimos el código podemos acabar generando líneas de una elevada longitud que resultan difíciles de leer. Poder acotarlas ayuda a la lectura (y comprensión) de código.

Las soluciones son dos (al menos): acortar cada línea en una orden independiente o acortar la línea mediante un procedimiento que no genere error al intérprete. Un ejemplo:

Supongamos que queremos escribir lo siguiente como cadena de texto: 

"Dime la figura sobre la que quieres calcular el área. Para ello utiliza el siguiente código: C -> Cuadrado, R -> Rectángulo, T -> Triángulo, C -> Círculo"

Podemos perfectamente dividir el texto en dos partes, la primera informativa mediante la función print():

print("Dime la figura sobre la que quieres calcular el área. Para ello utiliza el siguiente código:")

... y la segunda mediante un input(), asociada a una variable para que cumpla su función (según se deduce de la configuración de la instrucción):

respuesta = input("C -> Cuadrado, R -> Rectángulo, T -> Triángulo, C -> Círculo")

De este modo evitamos crear una línea excesivamente larga que dificulta la lectura del código, aunque puede que la primera exceda, aun así, los márgenes aceptables para un código legible. Si así fuera considerado, bien podría dividirse en al menos dos print() diferenciados, solucionando así el problema:

print("Dime la figura sobre la que quieres calcular el área.")
print("Para ello utiliza el siguiente código:")

En términos de legibilidad del código, la solución es perfectamente válida, pero nos crea un ligero problema, poco relevante aquí, pero no tanto en programas de muchas líneas y cadenas de texto largas: necesitamos dos o tres instrucciones para resolver el problema. Si esto lo es, también podemos recurrir a otra solución aceptable para el intérprete Python: utilizar como separador de líneas que mantiene la integridad de código como una unidad (una sola orden):

    respuesta=input("Dime la figura sobre la que quieres calcular el área"\
                "Para ello utiliza el siguiente código:"\
                "C -> Cuadrado"\
                "R -> Rectángulo"\
                "T -> Triángulo"\
                "C -> Círculo")

La estructura creada gana en legibilidad y elegancia a cista de multiplicar la concatenación de segmentos (cadenas), pero tiene un problema: la salida (output) por pantalla no resulta funcional. Digamos que trasladamos el problema de la legibilidad al output (del programa), lo que dificulta el input (para el usuario)

Dime la figura sobre la que quieres calcular el área Para ello utiliza el siguiente código: C -> Cuadrado R -> Rectángulo T -> Triángulo C -> Círculo

Este problema (al menos en parte) ya lo habíamos podido observar en la versión primera del input(), pero ahora se revela en toda su extensión y resulta claramente inadmisible. Afortunadamente tenemos una solución: utilizar un salto de línea incorporado a cada string (\n). Esta opción produce el efecto deseado en la salida del programa (el salto de línea) allí donde se ubica, con independencia de la posición que ocupe en el string que escribimos como código. En este caso nos interesa la coincidencia del fin y salto de la línea de código con el salto de línea del output (en pantalla), pero esto es circunstancial.

    respuesta=input("Dime la figura sobre la que quieres calcular el área"\
                "\nPara ello utiliza el siguiente código:"\
                "\nC -> Cuadrado"\
                "\nR -> Rectángulo"\
                "\nT -> Triángulo"\
                "\nC -> Círculo"\
                "\nInicial de la figura: ")
   
Lo importante es que la combinación de ambos recursos (\ para separar líneas sin romper la unidad de la instrucción, y "\nString..." para generar saltos de línea en pantalla) nos permiten obtener un código legible (como el de arriba), a la vez que una salida por pantalla igualmente legible con una única instrucción input()

Dime la figura sobre la que quieres calcular el área
Para ello utiliza el siguiente código:
C -> Cuadrado
R -> Rectángulo
T -> Triángulo
C -> Círculo
Inicial de la figura:

Introducción. Modelos de programación.

POO. Métodos.

Cuando hablamos de los métodos como componentes de una clase, asimilamos éstos con las funciones, llegando incluso a insinuar que eran denominaciones intercambiables. Así son considerados por muchos autores, aunque puede que no de forma explícita, y no faltan razones para ello, ya que básicamente un método es una función; no obstante, el ser parte constituyente de una clase otorga a los métodos algunas diferencias respecto a las funciones que es necesario conocer para entender el código de terceros y para trabajar adecuadamente con ambos.



En esta entrada vamos a exponer las diferencias existentes entre una función y un método, las cuales radican fundamentalmente en el uso de parámetros y en las implicaciones que tiene la pertenencia del método a la clase para su uso por parte de los objetos.

La primera diferencia que debemos señalar entre una función y un método es que en los métodos no vamos a encontrarnos nunca con el paréntesis vacío: como mínimo (y muy frecuentemente, como único) nos encontraremos con el parámetro self, que referencia a la clase y en su momento al objeto).

La segunda diferencia es que aunque el método utilice parámetros/atributos de la clase, no es necesario (y no se debe) establecerlos como tales parámetros, siendo suficiente con utilizar la formula self.nombre_atributo dentro de la función para que ésta utilice adecuadamente el valor que el parámetro tenga en el objeto.

La tercera diferencia es que para llamar al método desde el objeto no es necesario incluir valor alguno como argumento: ni para self (ya que es el propio objeto) ni para los argumentos (ya que están definidos previamente en la construcción del objeto.

Bien podría ser que el método en su formulación abstracta (como parte de la clase)  cuente con parámetros o con variables específicas (y privadas) no contempladas como atributos. En ese caso deberemos proceder del mismo modo que hacemos con las funciones tanto en la construcción del método como en el momento de llamarlo desde (y en función del) objeto.

Vamos a presentar un ejemplo en el que el método de una clase cuenta con un parámetro no incluido como atributo (aunque en realidad bien podría serlo) y una variable privada. Podemos comparar el funcionamiento de este código con el que se desarrolló en su momento para ver unas diferencias que explicaremos a continuación.

En el archivo actual, ni los argumentos ni el constructor de la clase se diferencia del archivo inicial... 

def __init__(self,nombre,edad):
        self.nombre = nombre
        self.edad = edad

... pero sí el método sentarse(). Observa:

Original: 

def sentarse(self):
        print(f"{self.nombre} se sienta cuando se lo ordeno (y quiere, claro).")

Actual:

 def sentarse(self,sexo):
        if sexo =="perrito":
            tratamiento = "educado"
        else:
            tratamiento= "educada"
        print(f"Mi {sexo} {self.nombre} tiene {self.edad} años. Está muy bien {tratamiento} y se sienta cuando se lo pido.")
  • Podemos identificar ambos como métodos (y no funciones) por el hecho de que ni en el primero ni en el segundo nombre y edad (ambos atributos identificados como tales en la construcción de la clase) son declarados como parámetros y por ser empleados dentro de print() mediante la auto-referencia (self.nombre, por ejemplo)
  • Además, el parámetro self presente en ambos métodos nos confirma que se trata de métodos, ya que este parámetro no se usa en funciones.
De hecho si formulásemos el método como función podríamos hacerlo como sigue (obviamente en este caso no se generan atributos ni se utiliza una función de inicialización):

def sentarse(nombre,edad,sexo):
    if sexo=="perrito":
        tratamiento = "educado"
    else:
        tratamiento = "educada"
    print(f"Mi {sexo} se llama {nombre} y tiene {edad} años. Es muy {tratamiento} ya que se sienta cuando se lo ordeno.")

Fíjate que, a consecuencia de ello, además de trabajar con los parámetros de una forma claramente diferente, en el print() de la función las variables incluidas como parte de la cadena f no se expresan mediante la sintaxis del punto, como sí hacemos en sus dos formulaciones como método.

Finalmente vamos a ver las diferencias en el modo de hacer la llamada a la función y al método.

Función (tras cuestionario asociado a variables/argumentos)

sentarse(perro_nombre,perro_edad,perro_sexo)

Método (formulación simple)

perrita = MiPerro("Atenea",10) -> Creación del objeto
perrita.sentarse() -> Llamada al método

Método (formulación similar a función, esto es: con cuestionario previo)

perro = MiPerro(perro_nombre,perro_edad) -> Creación del objeto
perro.sentarse(perro_sexo) -> Llamada al método

Al margen de lo que implica trabajar con clase-objeto, primero nos vamos a fijar en las similitudes y diferencias entre la llamada a la función y la llamada al método: en realidad llamar a la función equivale a desarrollar en una única instrucción el doble proceso de creación de objeto y llamada al método que se produce cunado trabajamos con clases y objetos.

La presencia necesaria de argumentos se concreta en la función en el momento de ser llamada, mientras que en el método se diferencia en función del doble proceso de creación del objeto, que se produce una vez y ya no tiene que volver a repetirse, lo que supone una ahorro importante de trabajo en caso de utilizar el objeto múltiples veces y con diferentes métodos, y la llamada al método propiamente dicho, que se resume como llamada a método sin más en su forma más simple, o que puede incluir algún parámetro en caso de haberse incluido alguno, como es el caso del segundo método.

Pero volvamos al análisis de la formulación del método en sus dos versiones: la más simple no incluye parámetros añadidos ni variables privadas, por lo que se presenta en su forma más simple, tanto en su definición como parte de la clase...

def sentarse(self):
        print(f"{self.nombre} se sienta cuando se lo ordeno (y quiere, claro).")

... como al ser llamada desde el objeto

perrita.sentarse()

La segunda formulación incluye el uso de un parámetro no establecido como  argumento en la definición de la clase (sexo), y una variable privada (tratamiento).

def sentarse(self,sexo):
        if sexo =="perrito":
            tratamiento = "educado"
        else:
            tratamiento= "educada"
        print(f"Mi {sexo} {self.nombre} tiene {self.edad} años. Está muy bien {tratamiento} y se sienta cuando se lo pido.")

Fíjate en las implicaciones de la presencia de este parámetro y de esta variable en cómo se escriben las variables en la cadena f: ninguno de los dos se formula con self., mientras  que sí se utiliza con las variables-argumentos.

Esta diferencia en el tratamiento se traslada después a la llamada al método, que incluye explicitar el argumento asociado al parámetro sexo:

perro.sentarse(perro_sexo)

Esta diferencia entre ambos métodos es más importante en lo que estamos tratando en la presente entrada que el hecho de que en la creación del objeto en una formulación introduzcamos directamente los valores que concretan los parámetros

perrita = MiPerro("Atenea",10)

Mientras que en la segunda asociemos dichos parámetros a sendas variables que nos permiten modificar de forma interactiva (input) el valor de los atributos.

perro_nombre = input("Nombre del perro: ")
perro_edad = eval(input("Edad del perro: "))
perro_sexo = input("perrito o perrita: ")

perro = MiPerro(perro_nombre,perro_edad)
perro.sentarse(perro_sexo)

Aunque esta formulación se asemeje aparentemente a la fórmula empleada en la función...

perro_nombre = input("Dime el nombre de tu perro: ")
perro_edad = eval(input("¿Cuántos años tiene? "))
perro_sexo = input("¿Es perrito o perrita? ")

sentarse(perro_nombre,perro_edad,perro_sexo)

... si te fijas bien hay una diferencia que revela que se trata de dos formas diferentes de abordar la cuestión: en la función empleamos los tres argumentos, mientras que en el objeto utilizamos dos argumentos para establecer el contenido de los parámetros en la construcción del objeto y utilizamos el tercero (perro_sexo) como parámetro del método, dado que (el método) cuenta con ese parámetro en su definición y lo utiliza para dar contenido a la variable privada que tiene el método (tratamiento)

Es posible que todo esto te haya parecido algo lioso, incluso una minucia poco relevante: no lo es, créeme: de no tenerse en cuenta se producirán errores que impiden que funcione todo correctamente y tú no comprenderás el código de terceros que empleen parámetros y variables dentro de los métodos.

Para ayudarte a comprender con ejemplos estas diferencias te dejo acceso a los tres archivos que me han servido de ejemplo:

lunes, 6 de febrero de 2023

Introducción. Modelos de programación.

 POO. Atributos

Aunque ya vimos que son los atributos, no está demás completar una información que resulta ser parcial, ya que la definición de atributo no se limita a lo expuesto... y tiene repercusiones.



Sigo en esto la explicación de Matthes, E (2021:186-191) en lo que deriva de las formas en que se puede plantear la modificación de atributos, con lo que, en principio, trataré sobre la definición de atributos y posiblemente en una próxima entrada sobre su modificación.

Los atributos son, como ya sabemos, los datos que caracterizan a una clase y se concretan como tales en los objetos o instancias de esa clase. Como tales datos se guardan en memoria, identificando esa dirección de memoria con un nombre de variable, de ahí que podamos redefinir los atributos como las variables que caracterizan al objeto.

Como tales variables, en el momento de la construcción de la clase (o lo que es lo mismo: en el momento en que utilizamos esa función especial a la que llamamos constructor, y que en Python se expresa como función __init__()) tenemos tres opciones para definir las variables/atributos:
  • Opción A, la utilizada en la entrada referenciada al inicio de la actual siguiendo a Matthes, E(2021:182-186), estableciendo los atributos como parámetros de la función, tras el parámetro self. 
  • Opción B, la que utiliza Cuevas, A (2016:186-192) en su completa y complicada explicación de cómo se construye una clase: incluyéndolas como parte del desarrollo de la función __init__() pero sin incluirlas como parámetros de la función-constructor.
  • Y la opción C, la mezcla de las anteriores: incluyendo unos como parámetros y otros según B.
Todas ellas (las opciones) son válidas y es posible encontrarlas como explicación en diferentes documentos, incluyendo los vídeos de You Tube. Lo que no siempre encontrarás es una explicación de por qué estas diferencias y qué implicaciones tiene. Vamos a hablar a continuación de esas diferencias y sus implicaciones.

En primer lugar, no existe (que yo sepa) implicación respecto al funcionamiento de la clase y de los objetos derivados de ella: tan clase y tan objeto) es una que utilice parámetros en el constructor como que no los use (al margen de self, claro, que es obligado). Pero las implicaciones son evidentes: si usamos parámetros "atributivos" en __init__() esto va a tener consecuencias en la sintaxis de escritura de la clase y de los objetos:
  • En cómo se escribe la función __init__():
    • Lo obvio: con/sin parámetros
    • Y en la forma en que inicializa el parámetro
  • Y en cómo se definen los atributos del objeto
  • Y, finalmente, ¿en cómo se aborda el cambio de esos atributos?
Veamos cada una de estas cuestiones en un ejemplo concreto como puede ser la clase perro utilizado en otra entrada. En ella se utilizaba exclusivamente la opción A.
def __init__(self,nombre,edad):
self.nombre = nombre
self.edad = edad 
  • Al declarar los atributos como parámetros (nombre, edad), inicializarlos consiste en referenciarlos a la propia clase mediante la expresión self.nombre = nombre
  • Por lo que crear un objeto exige responder a la lógica de la relación parámetro-argumento que ya vimos en las funciones, incluyendo el dato en que se concreta el atributo: MiPerro(Atenea,10)
Frente a esto, la opción B resolvería la función __init__() y la inicialización de los atributos como sigue:

def __init__(self):
self.nombre = ""
self.edad = 0 
  • La inicialización de los atributos (de clase) no es una auto-referencia como cuando los declaramos como parámetros (self.nombre = nombre). Aunque seguimos utilizando la auto-referencia y la sintaxis del punto (de pertenencia jerárquica, podríamos decir) (self.nombre), ya no podemos remitir a la propia variable (que después se concretará como dato-argumento en la declaración del objeto); debemos inicializarlo con un valor (que puede ser 0 y su equivalente: cadena vacía, como es el caso self.nombre = "")
  • Al crear el objeto ya no podemos incluirlos como argumentos en la función correspondiente, como si sucedía en el modelo A (perrita = MiPerro("Atenea",10))
  • Mejor habría que decir "ni debemos", ya que en A, no emplear estos argumentos genera error. En B sucede lo mismo si lo hacemos (ya que no existen como parámetros en __init__()): perrita = MiPerro() es ahora la forma de crear el objeto como perteneciente a la clase MiPerro, el cual queda identificado mediante la variable que lo referencia (perrita) ya que, en caso de no realizarse esta asignación del objeto a la variable, se crearía el objeto, pero como objeto anónimo. Obsérvese que tras el nombre de la clase se sitúan dos paréntesis [MiPerro()], mientras que en la identificación de la clase no se hace uso de tales (class MiPerro:). Por contra, no se utilizan los dos puntos (:), que sí lo están en la definición de la clase (y de las funciones): en resumen, estamos usando la sintaxis de la llamada a una función.
  • La alternativa es asignar contenido a los argumentos utilizando la sintaxis del punto, tomando como referencia el nombre del objeto al que se asocia el argumento en la definición de la clase [perrita.nombre = "Azucena"]. De ello se deriva que mientras en A la creación del objeto y la asignación de valores a los atributos se resuelve como parte de la llamada a la función (identificación como miembro de la clase...
perrita = MiPerro("Atenea",10)

... en B primero se define la relación de pertenencia del objeto a la clase y después se asignan datos/valores a los atributos, que se identifican como tales (y no come meras variables) por medio de la sintaxis del punto:

perrita = MiPerro()
perrita.nombre = "Azucena"
perrita.edad = 10 

[Nótese la ausencia de sangrado y lo que esto supone entre la línea de creación del objeto y las de datación de los atributos]

Los resultados de ambos procedimientos (A y B) son los mismos si aplicamos las mismas demandas:

print(f"Nombre: {perrita.nombre}")
print(f"Edad: {perrita.edad}")
print(f"Mi perrita se llama {perrita.nombre} y tiene {perrita.edad} años")
perrita.sentarse()

Nombre: Atenea
Edad: 10
Mi perrita se llama Atenea y tiene 10 años
Atenea se sienta cuando se lo ordeno (y quiere, claro).

... y cabe entender que el uso del modelo mixto (C) supone aplicar ambos procedimiento tanto en la inicialización de los atributos como en la creación del objeto. Un ejemplo sencillito, añadiendo sexo como tercer parámetro. Supongo que intuyes cual puede ser el resultado...

class MiPerro:
    def __init__(self,nombre,edad):
        self.nombre = nombre
        self.edad = edad
        self.sexo =""
    def sentarse(self):
        print(f"{self.nombre} se sienta cuando se lo ordeno (y quiere, claro).")

perro = MiPerro("Casimiro",10)
perro.sexo = "perrito"

print(f"Nombre: {perro.nombre}")
print(f"Edad: {perro.edad}")

print(f"Mi {perro.sexo} se llama {perro.nombre} y tiene {perro.edad} años")

perro.sentarse()

domingo, 5 de febrero de 2023

Introducción. Modelos de programación.

POO y lenguajes de programación

Centrar el estudio de la POO en Python supone hacer una distinción clara entre este lenguaje y los otros que tratamos en este blog, rompiendo el equilibrio mantenido hasta el momento. Las razones de esta decisión se explican (brevemente) en esta entrada.


Hasta donde yo sé y he podido llegar, PSeInt no dispone de estrategias específicas para abordar el aprendizaje de la Programación Orientada a Objetos, al contrario de lo que sucede con la programación funcional (las bases de la programación funcional), así que no podemos partir del trabajo en pseudocódigo para abordar la comprensión de esta lógica de programación.

No se puede decir que otro tanto sucede con OOo Basic, aunque tampoco sería correcto decir que en este lenguaje podemos trabajar desarrollando código basado en la POO. Ciertamente OOo Basic supone una curiosa mezcla de lenguaje (de script) que sí trabaja con objetos, pero que no puede generar clases ni objetos. Lo primero se asocia a que se enmarca en un soporte (Open Office, primero, Libre Office ahora) basado en Java, lenguaje que no trabaja en otro paradigma que no sea POO, por lo que en OOo Basic nos encontramos muy frecuentemente trabajando con y sobre objetos, sus atributos y sus métodos, de hecho, cada vez que hacemos referencia a un elemento de la suite: un documento, una hoja, una celda, un marcador... y así sucesivamente, incluyendo el uso de la sintaxis del punto; Pero OOo Basic no genera clases ni objetos, a lo sumo podemos trabajar (igual que PSeInt) con variables (incluyendo las de tipo objeto), subrutinas y con funciones.

Finalmente, Python es en realidad un lenguaje multiparadigma, incluyendo recursos para la POO. Aunque no se puede decir que sea un lenguaje orientado a la POO (y alguna limitación presenta), como puede ser Java, así como tampoco es un lenguaje orientado al paradigma funcional (como sí lo es Haskell, por poner un ejemplo claro), aunque cuente con recursos propios de este enfoque, como las funciones lambda.

No obstante, Python nos permite crear clases y objetos, acceder a bibliotecas de terceros basadas en clases y objetos y desarrollar aplicaciones basadas en la programación orientada a objetos (POO), por lo que todo lo que se refiere a este enfoque, salvo conceptos básicos y generales, o aclaraciones como la presente, serán desarrolladas desde Python. Lo que espero es que comprender la POO me ayude a introducirme en Java (y hacer algún pinito en este blog con este lenguaje) y a usar OOo Basic con más conocimiento de causa en cuanto que este lenguaje maneja objetos, sus atributos y sus métodos.

sábado, 4 de febrero de 2023

Python. Lenguaje

Datos alfanuméricos. Cadenas f

En la segunda entrada dedicada a la POO pudimos ver un modo peculiar de escribir el contenido de una instrucción print(): print(f"{self.nombre} se sienta cuando se lo ordeno (y quiere, claro)."). Se trata de la llamada cadena f, formato en uso desde la versión Python 3.6, que sustituye al método format(), y que, al igual que éste, permite utilizar directamente variables dentro de cadenas.

Pero antes de continuar es necesaria una aclaración: tanto format() como las cadenas f nos remiten a las variables alfanuméricas, y más concretamente a los string como objetos, instancias de una clase (la clase string), por lo que format() se considera método específico de esta clase, al igual que otros muchos, como upper() y lower()tittle() y otros muchos otros. Ahora no me voy a detener en ellos, pero te dejo acceso a esta página en la que puedes entrar en mas detalle sobre esos métodos y la forma en que se usan. También te recomiendo esta otra página, mucho más completa, en la que puedes encontrar información técnica sobre los string y esta otra relacionada con la anterior y más genérica aun, sobre la biblioteca estándar de Python.

Volviendo a las cadenas f y su uso para concatenar cadenas incluyendo variables, recuerda que ya en OOo Basic nos mostramos especialmente interesados en este tipo de operaciones, debido a que, por funciones, trabajamos con frecuencia con documentos y necesitamos métodos prácticos y funcionales para componer textos. Un ejemplo de ello es construir un documento-tipo y personalizarlo a partir de información contenida en variables. 

El procedimiento combinar correspondencia es una forma de abordarlo que ya hemos utilizado como solución en las propuesta del blog OrientAsLO. También hemos utilizado procedimientos de construir textos mediante la unión de cadenas y variables en Python, pero no haciendo uso de las cadenas f, así que me parece de interés que nos detengamos brevemente en ellas para comprender su funcionamiento.

Las cadenas f permiten introducir variables dentro de cadenas, simplificando y sobre todo, clarificando procedimientos de mayor más comunes, pero menos eficiente. Veamos cómo proceder suponiendo la creación de un script que genera un texto personalizado a partir de un documento-base como los que generamos como docap mediante OOo Basic. Aunque me limitaré a un único párrafo (por no alargar innecesariamente la propuesta), utilizaré los dos procedimientos disponibles en Python (el ordinario de concatenación y las cadenas f) para comprobar la funcionalidad de ambos procedimientos.

El texto-base es el siguiente:

Observaciones: Este informe se realiza a demanda de la Dirección del centro (12/10/2022) por petición de  tutoría, formulada mediante PROPUESTA DEL EQUIPO DOCENTE DE SOLICITUD DE EVALUACIÓN PSICOPEDAGÓGICA (10/10/2022).

Este texto, aunque no excesivamente, es suficientemente complejo para que resulte interesante explorar las opciones de concatenación de texto según lo formulado antes como propuesta: contiene el volumen suficiente de texto fijo y de texto variable (a tratar mediante variables), y supone un ejercicio de concatenación de variables tipo string.

Suponiendo las variables siguientes y sus valores asignados...

demandante = "la Dirección del centro"
fechaDemanda = "12/10/2022"
peticion = "tutoría"
fechaPeticion = "12/10/2022"
fechaInforEqDoc = "11/10/2022"

... la formulación mediante cadenas concatenadas es como sigue:

print("Observaciones: Este informe se realiza a demanda de ", demandante, "(", fechaDemanda,") por petición de ", peticion, "(", fechaPeticion, "), mediante PROPUESTA DEL EQUIPO DOCENTE DE SOLICITUD DE EVALUACIÓN PSICOPEDAGÓGICA (",fechaInforEqDoc,")")

... mientras que usando cadenas f se construye del siguiente modo:

print(f"Observaciones: Este informe se realiza a demanda de {demandante}, ({fechaDemanda} ) por petición de {peticion} ({fechaPeticion}, mediante PROPUESTA DEL EQUIPO DOCENTE DE SOLICITUD DE EVALUACIÓN PSICOPEDAGÓGICA ({fechaInforEqDoc})")

En ambos casos el resultado es el mismo:

Observaciones: Este informe se realiza a demanda de la Dirección del centro, (12/10/2022 ) por petición de tutoría (12/10/2022, mediante PROPUESTA DEL EQUIPO DOCENTE DE SOLICITUD DE EVALUACIÓN PSICOPEDAGÓGICA (11/10/2022)

 ... pero el uso de la cadena f permite que nos olvidemos del tedioso y confuso proceso de  abrir y cerrar comillas (que en la concatenación clásica identifico mediante la escritura en dos colores), fuente de errores, lo que hace que sea más sencillo y fluido escribir el texto final dentro de la función print().

Introducción. Modelos de programación.

POO. Clase y objeto

Después de introducirnos en los conceptos básicos de la POO, en esta entrada vamos a aprender a identificar y crear (en abstracto) una clase y un objeto o instancia de esa clase. Esto nos servirá de base para iniciarnos en la práctica de la POO en lenguajes de programación, y más concretamente en Python.




Una clase se define, como vimos en la entrada anterior, por ser una representación abstracta de objetos concretos que son, a su vez, instancias o concreciones de esa clase. La clase posee características (en POO atributos) y funcionalidades (que se concretan mediante funciones y que en POO se denominan métodos). La POO permite tratar ambas (características y funciones -> atributos y métodos) como un todo, lo que facilita el manejo del código.

Para crear una clase debemos utilizar la palabra reservada (por ejemplo Class), y darle un nombre identificador. Por convención, los nombres de las clases se inician con mayúscula, lo que la diferencian del nombre del objeto y de los métodos de la clase. Según los lenguajes, este proceso inicial puede precisar ser marcado de algún modo, por ejemplo, mediante (:) En Python...

class ClaseNombre:

El paso siguiente es utilizar un constructor de la clase, variando de procedimiento según en el lenguaje. En Python se realiza como sigue:

  • definimos una función especial que consta de las siguientes partes:
    • El definidor def 
    • El nombre de la función __init__, que se caracteriza por ir precedido y seguido por dos guiones bajos sucesivos.
    • Y la definición de parámetros, que inician por convención por el parámetro self, que hace referencia a la clase y a su constructor.
    • finalizando mediante :, como cualquier otra función.

def __init__(self,param1,param2):

  •  referenciamos los parámetros que nos permiten identificar y dar valor inicial a los atributos de la clase, utilizando la referencia a la propia clase y a su constructor, a la que queda asociado el atributo mediante la sintaxis del punto:

self.param1 = param1
self.param2 = param2 

Finalmente creamos los métodos, de modo similar a como creamos las funciones. Estos métodos pueden contar con parámetros (o no) y devolver (return) datos (o no). En su formulación más simple (sin parámetros ni retorno) se expresan (en Python) como sigue:

def metodo1(self):
contenidoDelMétodo

Instanciar un objeto de la clase anterior consiste, básicamente, en hacer referencia de esa clase a la hora de declarar el objeto para posteriormente acceder a sus atributos y a sus métodos recurriendo a la sintaxis del punto.
  • Nombramos (declaramos) la instancia u objeto asociándolo a modo de variable a la clase y concretando como argumentos sus atributos. En Python...
mi_objeto = ClaseNombre(ValorArg1,ValorArg2) -> Los argumentos son concreciones de los
atributos de la clase en el objeto.
  • Accedemos a los atributos mediante la sintaxis del punto
mi_objeto.param1 (Recuerda que el parámetro es en realidad el atributo1 de la clase que se ha
           concretado en la creación  del objeto como ValorArg1)
  • y de forma similar a los métodos
mi_objeto.metodo1()

Como un ejemplo puede aclarar mucho más las cosas, paso a desarrollar uno a partir de Python. El objetivo es mostrar el desarrollo de una concreción del proceso anterior siguiendo las explicaciones que proporciona Eric Matthes (2020:181-185)

  • Clase Perro

class MiPerro:

 def __init__(self,nombre,edad):

self.nombre = nombre

self.edad = edad 

 def sentarse(self):

         print(f"{self.nombre} se sienta cuando se lo ordeno (y quiere, claro).")

  • Instancia u objeto de la clase MiPerro

perrita = MiPerro("Atenea",10)

  • Acceso a los atributos de perrita:

 print(f"Mi perrita se llama {perrita.nombre} y tiene {perrita.edad} años")

  • Acceso al método creado en la clase

perrita.sentarse() 

Puedes comprender que es posible desarrollar clases mucho más complejas en cuanto a atributos y métodos, así como crear tantas instancias de la clase (objetos) como necesitemos en nuestro programa, pero para el propósito de esta entrada es suficiente con lo anterior. Su resultado (output) es el siguiente:

Mi perrita se llama Atenea y tiene 10 años
Atenea se sienta cuando se lo ordeno (y quiere. claro).