viernes, 30 de diciembre de 2022

Python. Interface.

TKinter.Presentación

Claro que se puede prescindir de una interfaz gráfica (GUI) para crear aplicaciones: sólo tenemos que trabajar mediante órdenes desde la consola. Pero hacerlo mediante una GUI resulta mucho más funciona y acorde con lo que se espera de una aplicación "moderna". Esto lo logramos interponiendo un conjunto de gráficos, lo que necesariamente complica el algoritmo, pero los beneficios compensan el esfuerzo.


Utilizaré tkinter (tk) por estar incorporado en Python como conjunto de instrucciones y funciones que permiten implementar los widgets (componentes) de tkinter como clases de del lenguaje. Esto remite al concepto de POO y requiere el conocimiento de las funciones, pero iniciaremos el aprendizaje de tkinter obviando estos fundamentos para lograr un nivel básico de capacidad de uso de esta herramienta GUI. Incluso, a la inversa, es posible que este proceso nos sirva para aprender algo sobre el trabajo con objetos y el uso de funciones, pero no es este el objetivo y esos conceptos reciben atención específica en este mismo blog. 

Antes de continuar concretando en qué se va a basar la parte práctica de este  enfoque, los siguientes enlaces remiten a la documentación que sirve de referencia.

Por lo que se refiere a este mi proceso de aprendizaje, me lo planteo en dos fases: 
En la primera, que es la que se inicia ahora, pretendo adquirir los conocimientos suficientes y necesarios para utilizar tkinter como herramienta para crear sencillas aplicaciones, dando una primera utilidad al aprendizaje de los rudimentos de Python.

Para ello seguiré el siguiente recorrido en el que diferencio, a su vez, dos etapas:

  • Aprender de forma sistemática a implementar los componentes básicos de tkinter.
  • Y ser capaz de crear sencillas aplicaciones de escritorio con los componentes estudiados.
Como puedes apreciar, la segunda de estas fases es algo así como la puesta en práctica de la primera, por lo que no se trata tanto de crear utilidades reales como de llevar a la práctica lo aprendido a nivel "teórico".

La segunda fase no tiene, en realidad, fecha de inicio, ni está definida en términos de objetivos y fases como sí lo está la primera. Se trata en realidad más de la culminación de un proceso que considero lógico y se concreta (mejor sería decir, se concretará previsiblemente) como aprendizaje sistemático y formal de tkinter como herramienta para la creación de GUI dentro de aplicaciones planteadas y diseñadas como herramientas funcionales para el desarrollo de la actividad profesional del SEO.

Es obvio que lo que resta para alcanzar este segundo nivel es mucho, por lo que no tiene sentido tratar de concretar algo más cómo se podría plantear esta segunda fase. Ni tan siquiera es seguro que se alcance, ni no llegar a esa meta me supone ningún motivo de frustración, ya que este proceso de aprendizaje no pretende ser otra cosa que un divertimento personal. Si alguien tiene la prisa que yo no tenga, tendrá que buscarse otros medios.

jueves, 29 de diciembre de 2022

Python. estructuras.

 Funciones en Python. Parámetros (2)

Ya al inicio de esta serie de entradas dedicadas a las funciones en Python hablamos de la estructura o, mejor dicho, estructuras posible de una función e indicamos que uno de sus componentes posibles (que no siempre presente) era los parámetros. Dedicaremos esta entrada a tratar sobre este componente y sus diferentes manifestaciones.


Como ya sabemos, un parámetro es una indicación o referencia a los datos que puede recibir una función y con los que opera. En las build-in functions, los parámetros pueden ser obligatorios u opcionales, pero en las funciones que creamos nosotros puede estar presentes o no (el juego de paréntesis que los alberga siempre lo estará, aunque sea vacío), pero en caso de estarlo son siempre obligatorios, por lo que, cuando llamamos a la función desde el script principal, deberemos incluir necesariamente tantos argumentos como parámetros tenga la función.

Por cierto, esta distinción entre parámetro y argumento no siempre se respeta a nivel terminológico, pero es conveniente conocerla para evitar confusiones terminológicas. El gráfico siguiente pretende servir a este fin.


NOTA 1. Cuando definimos la función y cuando trabajamos con ella, hablamos de parámetros, mientras que cuando la llamamos desde el script principal (o desde otra función), hablamos de argumentos.

Aclarada esta cuestión, seguimos con el uso de argumentos en la llamada a la función y su relación con la declaración de parámetros que hacemos en ella, ya que una cuestión de interés es que, aunque en las funciones creadas es obligatorio utilizar tantos argumentos como parámetros tenga la función, esto no implica que deba hacerse necesariamente en el mismo orden; si bien esto es lo común, no es la única opción.

De hecho podemos diferenciar entre solicitud mediante argumentos posicionales y mediante argumentos nombrados.
  • Los argumentos posicionales son los que respetan el orden y el tipo de parámetros declaramos en la función.
  • Los argumentos nombrados utilizan para expresarse el par clave-valor, siendo la clave la denominación que recibe el parámetro en la declaración de la función.
Al utilizar esa clave como referencia se facilita que la función establezca la identificación del valor o dato con el parámetro, de modo que no queda limitada a asociación a la igualdad de posición en los órdenes respectivos de argumentos y parámetros. Aunque podemos utilizar ambas peticiones en el mismo llamamiento a la función, debemos tener en cuenta que existen algunas restricciones, como veremos en el ejemplo siguiente:
 
def opera_num(num1,num2,num3):
    total = (num1 - num2) * num3
    print(total)

opera_num(23,15,7)
opera_num(num3 = 23, num1 = 15, num2 = 7)
opera_num(23,15, num3 = 7)
opera_num(num3 = 23, num2 = 15, num1 = 7)
opera_num(num1 = 23,15, 7)

Esta función (opera_num()) consta de tres parámetros (num1,num2,num3, todos ellos datos numéricos para simplificar) y realiza una operación ( total = (num1 - num2) * num3) con los datos que se le  pasan desde el script principal (en este caso). No tiene instrucción return, pero sí ejecuta la función print(total)

Debajo presentamos diferentes formas de realizar la asignación de argumentos:
  • La primera es posicional pura (opera_num(23,15,7)) y devuelve 56.
  • La segunda lo es de palabra clave o nombrada pura (opera_num(num3 = 23, num1 = 15, num2 = 7)) y devuelve 184.
  • También lo es la cuarta (opera_num(num3 = 23, num2 = 15, num1 = 7)), que devuelve -184.
  • La tercera (opera_num(23,15, num3 = 7)) es mixta y devuelve 56.
  • Y la quinta provoca error por incumplir la restricción que presenta la solicitud por nombrado: no se puede realizar una solicitud por palabra-clave seguida de una por posición (sí al contrario). Esta quinta formulación bloquea la ejecución del script

Dejo cerrada esta entrada, aunque no se agota con ella el análisis de los parámetros de las funciones ni mucho menos el estudio de las propias funciones. Aun hay camino por recorrer, pero paso a paso.

jueves, 22 de diciembre de 2022

Python. Estructuras.

Funciones en Python. Entrada y salida

Aunque las hayamos usado con frecuencia y nos sean conocidas, no por ello estas funciones merecen menos nuestra atención, antes al contrario.

Me estoy refiriendo a las dos funciones básicas que nos facilitan al entrada de datos por teclado (input()) y la salida por pantalla (print()). Ambas pertenecen al conjunto de built-in functions relacionadas con las cadenas de caracteres y las variables string, y dada su importancia para el input y el output, he creído necesario tratarlas de forma específica.

Además de los datos que introducimos directamente cuando programamos un script desde el IDE, cuando necesitamos que sea el usuario o usuaria quien introduzca datos, la forma más simple de hacerlo (otras requieren el uso de interfaces graficas) es mediante la función input(), cuya formulación básica ya muestra que se trata de una función asociada a los datos alfanuméricos:

input("Dame un número: ") -> devuelve Dame un número: 4 -> '4'

Esta vinculación se observa aun más claramente cuando asignamos el resultado de la función a una variable para contener en ella el datos pasado por el usuario o usuaria. Por cierto, como es fácil de entender, esta es la forma más lógica y más usada de utilizar la función input() y no la que utilizamos antes.

num1 = input("Dame un número: ")
print(num1 * 2)

Si damos 23 como respuesta, el programa dará como salida 2323, esto es: duplicará la cadena inicial, no multiplicará el número 23 por dos, ya que 23, en esta formulación de la función input() es un dato alfanumérico, una cadena, no un número.

Si lo que deseamos es que num1 contenga un dato numérico, primero deberemos convertir ese dato alfanumérico a dato numérico, bien convirtiendo el tipo de variable a posteriori...

num1 = input("Dame un número: ")
num1 = int(num1)
print(num1 * 2)

... o bien directamente en la orden de asignación de la variable.

num1 = int(input("Dame un número: "))
print(num1 * 2)

En ambos casos, dando 23 como respuesta obtendremos como salida 46, que es el resultado esperado de multiplicar 23 x 2.

NOTA 1También podemos utilizar la función eval() en lugar de int(). De este modo el programa funcionará aunque se introduzca un número real (v.g. 23.12), cosa que no sucederá en caso de utilizar int(). Las razones se explican en esta entrada.

Por su parte print() es, en realidad, una función más compleja de lo que parece teniendo en cuenta el uso simplificado que solemos hacer de ella, pero este suele ser suficiente para el objetivo principal de esta función, así que nos limitaremos a explicarla con cierto detalle.

En este sentido print() nos permite la escritura de los datos que le pasamos directamente como argumento, o el cato o datos contenido/s en la variable. en este sentido las dos expresiones siguientes son equivalentes a nivel funcional:

print(123) -> devuelve 123

a=123

print(a) -> devuelve 123

Por frecuente que resulte esta formulación, lo cierto es que print() consta de varios parámetros, algunos de ellos complejos, pero poco usados, por lo que no es frecuente encontrarse con ellos expresados de forma explícita:

print(*objects,sep=' ',end='\n',file=sys.stout,flush=false)

  • El primer parámetro (*objects) se formula de este modo para indicar que es posible pasar un número variable de datos de diferente tipo, tanto directamente, como por medio de variables.
print(1+2+3) -> devuelve 6
print("dame la mano"+" "+"mi hermano") -> devuelve dame la mano mi hermano
print(str(1)+" dame la mano "+str(2))-> devuelve 1 dame la mano 2
print('1'+'2'+'3') -> devuelve 123

NOTA 2. Aunque print() admite diferentes tipos de variables, en un uso concreto todas ellas han de ser del mismo tipo (en caso contrario da error). La tercera línea es un ejemplo de ello: hemos tenido que utilizar la función str() para convertir los números en datos alfanuméricos y evitar así el mensaje de error. Frente a ello, la primera línea evidencia que print() admite valores numéricos  y las operaciones que éstos pueden realizar, al igual que valores alfanuméricos (línea 4). En este caso, + pasa a ser un operador de concatenación, en lugar de la función de operador algebraico que asume en la línea primera.

  • El segundo parámetro (sep=' ' como ejemplo) establece el separador entre los elementos del primer parámetro. De no usar este segundo parámetro, los elementos del primero se mostrarán juntos...

print("Buenos dias"+"amigo Carlos" -> devuelve Buenos diasamigo Carlos

... por lo que si lo queremos evitar deberemos incorporar la solución dentro de cada elemento del primer parámetro...

print("Buenos días "+"amigo Carlos") -> devuelve Buenos días amigo Carlos

... o como un elemento más.

print("Buenos dias"+" "+"amigo Carlos") -> devuelve Buenos dias amigo Carlos

Utilizando el parámetro sep, se establece esta separación de forma mucho más eficiente y flexible. Además disponemos de dos soluciones:

    • Utilizar el valor por defecto (un espacio de separación) que se activa cuando separamos los elementos del primer parámetro mediante comas.

print("Buenos dias","amigo Carlos") -> devuelve Buenos dias amigo Carlos

... lo mismo que 

print("Buenos dias","amigo Carlos",sep=' ') -> devuelve Buenos dias amigo Carlos

    •  por lo que podemos utilizar otro formato para la separación:

print("Buenos dias","amigo Carlos",sep=' ---') -> devuelve Buenos dias ---amigo Carlos 

  •  El tercer parámetro que analizaré es el de finalización del segmento textual (end='\n', por ejemplo). este parámetro establece la separación entre la salida de una función print() y la siguiente, siendo por defecto (cuando no se indica expresamente) la secuencia de escape o salto de línea.

print("Buenos días")
print("Buenas noches")

devuelven...

Buenos días
Buenas noches

mientras que..

print("Buenos días","amigo Carlos")
print("Buenas noches")

devuelve

Buenos días amigo Carlos
Buenas noches

En el primer ejemplo, la ausencia de explicitación del tercer parámetro no altera el resultado: tras la ejecución de la primera función print() se produce un salto de línea; también en el segundo ejemplo, sólo que en este también provocamos la ausencia del segundo, observando que del tercero se deriva el salto de línea y del segundo la separación de los dos componentes del primer parámetro mediante un espacio. En el ejemplo que sigue utilizaremos el parámetro end 0ara introducir otra forma de separar lo resultante de dos print() sucesivos, en este caso mediante una tabulación.

print("Buenos dias",end='\t')
print("Buenas noches")

 devuelve Buenos dias Buenas noches

NOTA 3. Además de para provocar salto de línea (\n) o salto de tabulador (\t) tras cada uso de la función print(), el carácter \ se usa también para hacer visible sin generar errores determinados caracteres, como, por ejemplo, las comillas (\" o \') dentro de un string o para  insertar caracteres en formato octal (\ooo), hexadecimal de 8 bits (\xhh) (ASCII) o hexadecimal del 16 bits (\uxxxx) (Unicode).

Dado que la opción por defecto es provocar un salto de línea tras cada print(), si deseáramos evitar este efecto y que los textos de sendos usos de la función se mantengan en la misma línea, podemos utilizar el parámetro end precisamente para inhibir su funcionamiento por defecto:

print("Buenos dias",end='')
print("Buenas noches")

devuelve Buenos diasBuenas noches

martes, 20 de diciembre de 2022

Python. estructuras.

Funciones de cadena en Python

Siguiendo con las funciones propias (Built-in functions) trataremos en esta entrada las funciones utilizadas con las cadenas de texto.


Y empezaremos por la función str() que permite convertir un valor numérico (entero o real) en un valor alfanumérico: str(96) convierte 96 en '96', pero si utilizamos una variable intermedia...

a=123
b= str(a)

... entonces a sigue siendo 123 (número) y b '123', lo que podemos comprobar si operamos con ambas variables

a*2 -> devuelve 246

b*2 -> devuelve '123123'

Las funciones ord() y chr() pueden considerarse complementarias: mientras ord() devuelve el código Unicode (valor numérico) del carácter que pasemos como valor, chr() devuelve el carácter que corresponde al número que pasamos como valor (en base 10). Veamos un ejemplo:

ord('c') -> devuelve 99

chr(99) -> devuelve 'c'

NOTA 1Recuerda que ord() sólo admite un único carácter (por lo que se escribe entre comillas), no un string, y devuelve un valor numérico tipo integer. Por su parte chr() admite un número, no un valor alfanumérico (por lo que no se escribe entre comillas) y devuelve un carácter.

Aunque los manuales incluyen otras funciones dentro de este apartado, prefiero dejar alguna para tratar de forma específica y remitir a esa misma documentación para el análisis de otras, como las funciones de formato (Alberto Cuevas (2016:76-78), por ejemplo.)

lunes, 19 de diciembre de 2022

Python. estructuras.

Funciones numéricas en Python

Vamos a dedicar una serie de entradas a hablar de las principales funciones propias (built-in functions) asociadas al manejo de datos, empezando por los datos numéricos.


La función int() permite obtener el valor numérico de tipo entero (integer) que le pasemos como parámetro, sea éste un dato alfanumérico o uno numérico real. 

int("24") -> devuelve 24

int(24.234) -> devuelve 24

Estos son, posiblemente, los usos más frecuentes de esta función (especialmente la de convertir a entero un dato alfanumérico), pero no los únicos, ya que int(x) cuenta en realidad con dos parámetros...

int(x, base = 10)  

... lo que permite utilizar int() también para realizar cambios de base en los números pasados como primer parámetro: int("24",16) -> Devuelve 36

Este no es un uso frecuente de la función, pero tampoco muy conocido, ya que normalmente empleamos int() con un único parámetro, pues si trabajamos en base 10 no es necesario especificar la base con la que se trabaja, como podemos ver en los dos primeros ejemplos.

La función eval() sirve para convertir una cadena alfanumérica que representa un número real en un número real, algo que no puede hacer la función int(). 

eval("23.23") -> devuelve 23.23

int("23.23") -> devuelve error (ValueError: invalid literal for int() with base 10: '23.23')

Si queremos convertir a valor numérico y extraer la parte entera de una cadena alfanumérica que representa un número real deberemos utilizar ambas funciones anidadas: int(eval("23.23")) -> Devuelve 23

En resumen, se puede decir que ambas funciones se complementan, resolviendo una lo que no le está permitido a la otra, por lo que es conveniente saber diferenciar las situaciones en las que podemos usar una u otra, o cuando es conveniente utilizar ambas.

NOTA 1. Alberto Cuevas (2016:61) resumen en una sencilla tabla la casuística específica de cada una de estas funciones.

Otra función numérica que nos interesa conocer es round(), que como su nombre indica, nos permite redondear un número real a un número determinado de decimales. Esta función dispone de dos parámetros de entrada, aunque el segundo es opcional: round(number,ndigits). El primer parámetro es el del número a redondear, y el segundo el número de dígitos decimales que tendrá dicho número una vez redondeado. En caso de no utilizar este segundo parámetro, el número real se trunca a entero más próximo. Veamos algunos ejemplos:

round(123.987) -> devuelve 124 pero round(123.499) -> devuelve 123

round(123.987,2) - > devuelve 123.99 round(123.125,2) -> devuelve 123.12

  • Estos ejemplos muestran que los criterios del redondeo no son iguales según que circunstancias, por lo que deberemos tenerlo en cuenta a la hora de utilizar esta función: mientras que el redondeo al número natural (cuando no empleamos el segundo parámetro) tiene como límite la cifra de las décimas 5 para redondear a la unidad superior y 4 para redondear a la inferior, en el caso de indicar decimales (usando el segundo parámetro), este límite se traslada al 5 como límite de la inferior y al 6 de la superior. 
NOTA 2. Se debe destacar que esta diferencia se produce en función del uso del segundo parámetro, incluso cuando éste se especifica con el argumento 0. En este caso, además, se mantiene la apariencia de número real, con presencia de un decimal (el 0). round(123.499,0) -> devuelve 123.0

La siguiente función abs(), que devuelve el valor absoluto de un número entero o real, esto es: haciendo abstracción de su signo (+ vs. -). Esta función tiene un único argumento: abs(x), siendo x el valor numérico. Veamos algunos ejemplos:

abs(-123.438) -> devuelve 23.438
abs(12) -> devuelve 12
abs(-12) -> devuelve 12
abs(+12) -> devuelve 12

Las dos siguientes funciones, max() y min(), hacen referencia a conjuntos numéricos, respecto a los cuales max() identifica el valor máximo y min() el mínimo. Su peculiaridad es precisamente que hacen referencia a una colección de números separados por comas, por lo que se pueden considerar como parámetros de la función, siendo en este caso, variables en número.

max(23,23.54,24,23.92) -> devuelve 24
min(23,23.54,24,23.92) -> devuelve 23

NOTA 3. Los números pueden ser indistintamente positivos o negativas, enteros o reales. Al igual que en el resto de las funciones, los números reales emplean el punto para separar la parte entera de la decimal. Esto es importante recordarlo para evitar errores de interpretación, especialmente en las funciones que emplean más de un parámetro, ya que los parámetros se separar mediante comas, lo que puede dar lugar a confusiones. 

Podemos aquí dar por concluida la revisión de las funciones relativas a datos numéricos, ya que aunque no las hemos visto todas, sí las más importantes. En realidad faltan todas las variaciones de formato que podemos aplicar a estos datos, pero son demasiado numerosas como para pretender abarcarlas todas en esta entrada, siendo preferible hacer referencia a una fuente que las sintetiza correctamente, como Alberto Cuevas (2016:65-69).

viernes, 16 de diciembre de 2022

Documentos. Derivación

Formulario de derivación del Equipo Regional 

Tratando de responder a posibles necesidades prácticas, se me ha ocurrido que el documento actualmente vigente de solicitud de colaboración al Equipo Regional de Atención al ACNEAE podría ser un buen ejemplo de utilidad del uso de OOo Basic.

El documento lleva por título DEMANDA DE COLABORACIÓN AL EQUIPO REGIONAL DE ATENCIÓN AL ACNEAE, es accesible desde la web del Equipo y tiene una pretendida funcionalidad de facilitación de la comunicación entre el ER y el resto de los SEO del Principado de Asturias. 

Su formato (documento Word) es de un formulario teóricamente pensado para uso tanto manual (documento impreso y cumplimentado a posteriori) como (posiblemente como forma prioritaria) para ser cumplimentado mediante el propio programa MS-Office (Word). 

En su momento ya analicé la ambigüedad de este tipo de formatos y los errores de diseño en que se suelen incurrir. El documento del ER no es, en esto, una excepción, ya que se incurre en formulaciones mixtas que terminan complicando más que facilitando el uso del mismo. Ejemplo de ello, lo que muestra esta captura:

  • Junto con un impecable uso de tablas para facilitar la estructuración del contenido, esta composición está correctamente diseñada para usar el documento en formato papel pero no para ser cumplimentado mediante el procesador de texto: las casillas de selección/verificación no son accesibles desde Word.
  • Ejemplos de este tipo de errores no es que sean especialmente abundantes en el documento, pero por la escasa frecuencia de este tipo de formulaciones (tres ítem), ya no hay ocasión en la que se pueda cometer este error que no se produzca, lo que es coherente con la previsión de un uso manual del documento.
  • No obstante, también hay ítem que parecen pensados para utilizar el procesador de texto, como los dos primeros de la segunda página, ya que no parece razonable pensar que el espacio reservado sea suficiente si los cubrimos a mano.
Veamos ahora un ejemplo de uso incoherente de tablas, que también los hay:

  • Otra vez nos encontramos con un diseño aparentemente pensado para ser usado manualmente, ya que el uso mediante Word no se ve facilitado ni en el dimensionamiento de los espacios de respuesta ni en los desplazamientos.
  • Por un lado se desperdicia espacio y por otro no se aprovechan las ventajas del uso de las tablas (desplazamiento mediante tabulador), lo que favorece que se desbarajuste el documento si se trabaja con el procesador de textos.
En resumen, este documento de derivación parece no haber sido repensado en términos de informatización de los procesos de comunicación entre Servicios, heredando formulaciones propias del uso manual que tuvo en su tiempo este tipo de materiales. Esto no facilita precisamente el logro de la finalidad que se pretende con él, ya que dificulta su manejo por parte de todos los profesionales que lo utilizan, incluyendo a los propios componentes del ER-ACNEAE.

Aunque no es el principal objetivo de esta entrada, como previo a éste, me considero obligado a ofrecer una opción que considero sí puede ser funcional para usar en soporte digital. Después crearé una alternativa pensada mediante OOo Basic.

El primer resultado de esta propuesta de modificación es este documento-base que, como se ve, lo que hace es redefinir la formulación original empleando sistemáticamente el formato tablas. De este modo, el documento, que se presenta dividido en tres partes (al igual que el original), se puede cumplimentar usando el procesador de texto sin más requisito que pasar de celda mediante el tabulador.

Dado que se pretende cumplimentar con el procesador de texto, los espacios reservados para las respuestas se ajustan al contenido sin ningún tipo de previsión al respecto, por lo que la apariencia del documento-base es una mera aproximación a lo que resulte de su uso en la práctica. No obstante, es posible que no se produzcan cambios significativo al respecto.

En este enlace te dejo copia al [documento-base] en formato .odt (LO-Writer). Recuerda que tienes que descargarlo y guardarlo en tu ordenador, pero también que tienes que tener instalado el [software Libre Office].

En buena lógica, el siguiente paso sería convertir el documento-base anterior en un formulario, lo que supone utilizar los controles propios de esta interfaz y su posterior conversión en un formulario .pdf (esta recomendable). Si alguien lo considera conveniente y lo solicita, no tengo ningún inconveniente en realizar una propuesta de este tipo, pero no es lo que entra dentro de mis planes en este momento, así que me ahorraré un trabajo que,  no obstante, considero puede ser de interés.

Lo que sí me voy a plantear es utilizar algunos de los recursos disponibles desde  la funcionalidad Grabar macro y/o OOo Basic para desarrollar una alternativa al uso del documento-base anterior. Para ello es conveniente analizar su naturaleza, ya que las alternativas disponibles son diversas y su idoneidad puede estar relacionada con el uso previsto para el documento en cuestión.

Partiré de que este documento va a ser utilizado en soporte informático (nada nuevo hasta ahora respecto al documento-base) y que posiblemente sea cumplimentado al menos por dos personas; OE y tutor/a (la firma de una tercera, no resulta especialmente relevante). (1)

Aunque las delimitaciones anteriores no condicionan en exceso ni cierran las opciones disponibles (un formulario sigue siendo una buena opción), en este caso voy a optar por utilizar las marcadores de texto como medio para facilitar la automatización de la escritura del documento y un sistema combinado de ventanas emergentes y/o cuadros de diálogo para recoger la información necesaria para cumplimentar el documento.

Esta alternativa es una concesión al aprendizaje de una estrategia concreta (la descrita), pero posiblemente sea una alternativa excesivamente costosas, por lo que no se debe considerar la idónea. Repito: un formulario podría ser suficiente en cuanto a prestaciones e incluso más funcional, al resultar menos costoso de construir. Los marcadores de texto y los cuadros de diálogo, por separado no constituyen novedad, pero sí la combinación de ambas técnicas, así como el código necesario para su funcionamiento.

Defiendo la necesidad de emplear marcadores por la escasa utilidad parece suponérseles o por mero desconocimiento, especialmente como recurso en documentos en los que es necesario cumplimentar información puntual a partir de un formato documental muy cerrado, similar en cuanto alternativa, a la utilidad de Combinar correspondencia, aunque ésta tiene la ventaja de contar con una base de datos como respaldo, lo que la hace útil para la generación masiva de documentos personalizados, pero no tanto para documentos como el presente, que no requiere producción masiva.

El hecho de que este documento pueda ser cumplimentado por varias personas hace que sea aun más atractivo el empleo de estrategias como la que propongo, sirviendo de paso como modelo para situaciones similares como la elaboración colaborativa de informes.

Partiendo de lo anterior, formularé en primer lugar la especificación de la supuesta demanda, para identificar el proceso a seguir según lo que podemos entender como aplicación (simplificada) de la lógica de diseño y desarrollo, incluyendo la de programación en sentido estricto. El output ha sido diseñado ya en la formulación del documento-base, por lo que los procesos anteriores hacen referencia al input y al procesamiento.

Especificación. Crear un soporte que permita el uso compartido pero diferenciado entre SEO y tutoría de un documento para la recogida de información de cara a solicitar la colaboración del ER para ACNEAE por parte de los SEO. La finalidad de esta solicitud es facilitar el uso de un instrumento que permita sistematizar el procedimiento de formulación de la citada demanda. A tal fin se ha elaborado un documento-modelo que se empleará como base para la elaboración del citado soporte (docap).

Fases del proceso
  • Primera. Partiendo del documento-base (desarrollado a partir del documento-modelo original) introducir las marcas que sean necesarias para facilitar la automatizar la ubicación de la información. Comprobar el correcto funcionamiento de estas marcas.
  • Segunda. Crear las interfaces necesarias, teniendo en cuenta el principio de simplicidad en el uso y la posible diferenciación entre al menos dos potenciales usuarios: OE y Tutor/a.
  • Tercera. Generar el conjunto de rutinas, subrutinas y funciones que resulten necesarias para conectar el sistema de entrada (interfaces) con el procesamiento y elaboración de datos para generar la salida necesaria quede como resultado el correcto funcionamiento del docap.
  • Cuarta. Comprobar el perfecto funcionamiento del conjunto y en diferentes condiciones de uso. En su caso, corregir los errores que se aprecien y abordar las mejoras que se consideren pertinentes.
  • Quinta. Pilotar el empleo del docap en contextos reales de uso, recoger la información relevante sobre su funcionalidad e incorporar las mejoras que se aprecien necesarias.
  • Sexta. Dar a conocer el docap como recurso para uso público mediante su inclusión en la página web del ER.
Esta formulación del proceso puede considerarse como teórica, por lo que me limitaré, por razones obvias, a desarrollarlo hasta la fase cuarta y en estos momentos me quedo con un primer desarrollo del docap que puede considerarse equivalente en funcionalidad y apariencia, a la que proporciona (en la práctica) el documento-base. 

Esto implica reconocer que podemos trabajar perfectamente con ese documento sin notar (aparentemente) ninguna diferencia respecto al docap, así que para quienes no estén interesados en ir un poco más allá de las formas y de las apariencias, y únicamente se preocupen por la funcionalidad, pueden quedarse perfecta y cómodamente con la citada formulación sin más preocupación.

Pero quienes sientan curiosidad e interés por profundizar en lo que aporta en realidad la formulación del documento como docap, diré que en realidad éste, aunque por ahora aun limitado, contiene un gran potencial de mejora  que, eso sí, hace falta explicar y evidenciar. De momento me limitaré a lo primero.

En efecto, utilizar un cuadro de diálogo como interfaz que concreta la fase de entrada (input) implica diferenciar esta fase de las subsiguientes (procesamiento y salida), lo que favorece que la fase output pueda desarrollarse de forma totalmente diferenciada en forma y contenido a lo que estamos acostumbrados. Esto se debe a que las formas de trabajo empleadas no diferencian input de output, resultando soluciones de compromiso entre ambos, posiblemente con predominio del input en cuanto a la forma y del output en cuanto al contenido. Un ejemplo de uso de la potencialidad que se deriva de esta diferenciación es su aplicación para la creación de informes de intervención a partir de cuestionarios, como por ejemplo, las entrevistas a familias o cuestionarios de observación

La segunda potencialidad que conlleva el algoritmo desarrollado tiene que ver con la potencial utilidad del uso de los marcadores de texto como referencias para la construcción de documentos. Y en principio se me ocurren dos líneas de desarrollo:
  • La construcción de documentos en los que colaboran o participan varios profesionales de forma sucesiva (no simultánea) y con aportaciones diferenciadas y complementarias.
  • Cumplimentar documentos personalizables basados en plantillas cuando no existe emisión masiva de documentos y el formato combinar correspondencia cuando no resulta pertinente.
Es posible ampliar el listado de potencialidades, incluyendo las resultantes de la combinación de ambas características, pero ya sería suficiente con desarrollar alguna de las citadas para comprobar que el esfuerzo de crear un docap basado en este algoritmo resulte "rentable". De ello me ocuparé en una próxima entrada.

De momento me contento con dejarte enlace a tres documentos en formato Writer que debes descargar:
  • El [original del ER], para que lo tengas como referencia y por si te sirve de modelo y/o te es útil para el trabajo. Ten en cuenta que es el oficial del ER, al menos a la fecha de descarga (15/12/2022)
  • El que llamé [documento-base], que no es otra cosa que la reformulación del anterior para ser utilizado mediante el procesador de texto. Su uso es tan simple que no requiere explicación alguna y no creo que utilizarlo suponga invalidar una posible demanda, así que lo puedes usar tranquilamente.
  • Y el [formato docap] basado en el anterior aunque pero se cumplimenta mediante cuadros de diálogo (tres, uno por página) que se activan con botones de formulario (comandos asociados a script) visibles pero no imprimibles. Su funcionamiento se basa en el código asociado al que puedes acceder desde el IDE. Aunque el documento que te entrego no lo es, te propongo que crees una versión de trabajo de tipo plantilla y que trabajes desde este documento (desde las réplicas que se generan al hacer clic en la plantilla), de este modo no se alterarán los marcadores y no se corromperá el funcionamiento del algoritmo. 

NOTAS

(1) Lo que sí es relevante es que parezca preverse firma y sello manual. A falta de un recurso mejor aconsejo utilizar una firma digitalizada, así como también un sello de igual naturaleza. De este modo no es necesario el gasto de papel y puede ser enviado directamente por e-mail. No aprecio que exista motivo para que sea ineludible el tratamiento manual al que parece destinado este documento.

martes, 15 de noviembre de 2022

Python. Estructuras

Funciones en Python

Después de haber visto qué son las funcionescómo se crean y su uso en OOo Basic, es momento de tratar el tema de las funciones en Python. Para ello, además de la información que proporciona la bibliografía, he seguido la excelente información que proporciona Sebastián J. Bustamante, de FreeCodeCamp.


Como en el resto de los lenguaje de programación, también en Python podemos diferenciar entre las funciones nativas (aquellas disponibles en el propio lenguaje) y funciones creadas o propias (aquellas que creamos o que han creado otros usuarios del lenguaje). En esta entrada vamos a hablar de cuestiones generales: cómo se crean y se usan las funciones y qué aportan al lenguaje Python.

Empezando por el final, podemos decir que las funciones cumplen en Python el mismo objetivo que en el resto de los lenguajes: facilitar el proceso de programación, creando subcomponentes reutilizables.

Dado que lo primero que encontramos en Python son funciones nativas (de hecho ya hemos usado unas cuantas), quizá es más conveniente explicar primero cómo se emplean; para ello nada mejor que retomar experiencias ya conocidas de uso de funciones nativas, por ejemplo, la función print(): es esta una función nativa muy utilizada y muy sencilla, por lo que resulta ideal para explicar el uso de una función, nada más sencillo, por otra parte, que escribir dentro del paréntesis que acompaña a la función el dato o datos que precisa la función para desarrollar su labor. Y dado que print() es una función que nos permite escribir algo en consola (o en la interfaz que usemos, lo que debemos es escribir dentro aquello que queramos sea devuelto por nuestro programa: 
  • print("Buenos días") escribirá el string Buenos días
  • print(4+3) devolverá 7 (resultado de la suma de los valores pasado)
Las funciones creadas por nosotros pueden incluir, lógicamente, el uso de funciones nativas, pero su forma de funcionar no siempre obedece a la misma lógica. Veremos algunas de estas diferencias, pero primero tendremos que aprender a crear funciones sencillas.

La sintaxis de una función propia en Python es como sigue:
  • Utilizamos la palabra reservada def, tanto para el caso de funciones que no tienen parámetros o no devuelven valores (los subprocesos en OOo Basic), como para aquellas que sí los tienen y devuelven valores (las function en sentido estricto de OOo Basic). En esto Python se asemeja al modo de operar de PSeInt, aunque restringiendo la expresión a la palabra reservada def
NOTA 1. Recuerda que en PseInt utilizábamos indistintamente las palabras reservadas Subproceso o Funcion, y que en OOo Basic usamos las palabras reservadas Sub, para las subrutinas y Function para las funciones.
  • ... seguida del nombre o identificador de la función: def diHola
  • ... seguido de dos paréntesis: def diHola()que pueden estar vacíos (como en el ejemplo anterior), o contener parámetros de entrada, como en este otro caso: def diHolaNombre(nombre)
  • ... y finalizando la línea con dos puntos: def diHola():, que son imprescindibles para que la función funcione (de no ponerlos da error)
  • Finalmente se escribe el código que ejecuta la función, siempre sangrado respecto a la posición de def), que puede ser muy simple o muy complejo, dependiendo de la función que creemos. Veamos algunas funciones simple.
Primer ejemplo. Función sin parámetros.

 def diHola():

 print("Hola, amigo")

Esta función (propia) utiliza la función nativa print(), no tiene parámetros y devuelve el string Hola amigo al ser llamada.

Para llamarla nada más simple que escribir su nombre, incluyendo el paréntesis vacío:

diHola() -> devuelve Hola, amigo 

 Segundo ejemplo. Función simple con un parámetro

def diHolaNombre(nombre):

print("Hola " + nombre + ", buenos días.") 

En este caso, la función cuenta con un parámetro, pero no con sentencia de retorno. Para llamarla deberemos proceder igual que antes, pero escribiendo algo dentro del paréntesis (que en el caso de ser una cadena deberá ir entre comillas...)

diHolaNombre("Carmen") -> devuelve  Hola Carmen, buenos días

Tercer ejemplo. Función con un parámetro numérico

...  pero que no las llevará en caso de valores numéricos, como en este ejemplo...

def tablaCinco(num):

                    print("Resultado: " , num * 5)

 .... que llamaremos mediante escribiendo un número dentro del paréntesis como único parámetro 

 tablaCinco(4) -> devuelve Resultado: 20

Cuarto ejemplo. Función con dos parámetros.

Y para finalizar este entrada introductoria, una última función que desarrolla la anterior. En este caso una función con dos parámetros y sentencia de retorno (observa que ahora no usamos la función print() y sí la palabra reservada return, que no es una función nativa, sino una expresión)...

def sumaNum(num1, num2):

 return num1 + num2

...que llamaremos como sigue (observa que escribimos ambos parámetros separados por coma, no mediante el símbolo de la suma, como hacíamos al usar directamente la función nativa print()...

sumaNum(3,7) -> devuelve 10

 ... aunque el resultado es equivalente a utilizar print(3+7)

NOTA 2. Cuando la función contenga parámetros, deberemos usar en la llamada tantos parámetros como contenga la función y del mismo tipo. De no hacerlo dará error.

sábado, 12 de noviembre de 2022

Python. Archivos.

Python. Acceso a archivos

Para completar el tema del manejo de archivos externos mediante Python, quedaba pendiente estudiar los métodos de escritura y lectura de ficheros (de texto plano). Dedicaremos esta entrada a trabajar sobre este tema.


En realidad ya hemos empleado estos métodos en las entradas dedicada a trabajar con archivos (de texto plano) externos, también cuando hablamos del uso de interfaces gráficas, pero no los tratamos de forma explícita y me parece necesario hacerlo para no pasar por alto contenidos de tanta utilidad como la escritura y lectura en ese tipo de archivos.

Hasta el momento hemos visto el modo de acceso para creación, lectura y escritura de y en archivos de texto plano, su sintaxis y el modo de actuación de estos modos, pero no hay que confundirlos con los métodos (funciones si se prefiere) de escritura y lectura. Los primeros generan el fichero y acceden a él, y según el modo de acceso están permitidas determinadas operaciones (de lectura y/o escritura), pero esto no implica que estén ejecutadas esas operaciones, como mucho únicamente condicionadas.

Por ejemplo, el método w (write) nos permite crear/abrir un fichero para escribir en él, pero de por sí no escribe nada, únicamente crea el archivo si es que no está ya creado y lo abre y sobreescribe (trunca) si está creado. Comprobemos este funcionamiento:

  • El script ficheros4Sobreescritura.py con el que trabajamos en una entrada anterior, utiliza en la función open() el método "w" (fcrear=open("pruebaCrear.txt","w")), con lo que, al correrlo se presentan dos posibilidades:
    • Que el archivo pruebaCrear.txt no exista, y entonces lo crea (y hasta ese momento no contiene nada)
    • O que exista, lo abra y borre su contenido.

Según está escrito este script, a continuación, y mediante el uso sucesivo de funciones de escritura .write() vamos introduciendo contenido en ese archivo, por lo que al finalizar la primera parte del proceso (fcrear.close()) el archivo creado contiene tres líneas de texto (las que dan contenido al método/función .write() empleado.

  • Pero, ¿ qué sucederá si omitimos el uso de .write()?. Sucede lo que indiqué antes: se crea/abre un archivo (pruebaCrear.txt) (de nuevo se presentan las dos opciones antes indicadas), y al finalizar el proceso (fcrear.close()), si abrimos el archivo .txt comprobaremos que está vacío. El motivo es obvio: no hemos escrito nada en él, lo que traducido a código significa que no hemos utilizado la función/método .write(). Esto nos permite comprobar que no es el método "w" quien escribe el texto, sino el método .write(). El script ficheros5Sobreescritura.py contiene la modificación del código que permite comprobar lo explicado antes; en él he comentado (anulado) las líneas de código en las que se emplea el método .write() para demostrarlo.
Entonces, ¿ qué es lo que hace "w"?. Hace lo que se espera de él:
  • Crea/abre el archivo*
  • Elimina el contenido del archivo abierto
  • Y, en consecuencia, sitúa el puntero al inicio del archivo.

¿Qué no hace?; escribir en el archivo, ya que esa es función es responsabilidad del método/función write()

(*)NOTA. En realidad, "w" actúa en función de que es llamado como método (de trabajo) de la función open(), que es la que se encarga de acceder al archivo externo. "w" da forma/ o concreta el modo en que se ejecuta la función open(), la cual tiene su contrario en la función close(), que cierra el archivo (borra de la memoria RAM la vía de acceso al archivo que abrió open())

Tanto "w" como "a" (apend) determinan el comportamiento del método/función de escritura write(): "w" borrar el contenido previo, por lo que sitúa el puntero al inicio del archivo, y "a" no borra el contenido existente y sitúa el puntero al final del archivo. Pero en realidad, la diferencia entre ambos radica en lo que ambos hacen con el contenido preexistente, no es dónde se sitúa el puntero, ya que "w" podemos entender que sitúa el cursor al inicio, pero también al final (del archivo), puesto que, en su caso, al borrar el contenido, coinciden el inicio y el final del archivo. 

Finalmente"x" cumple la misma función que "w", pero está más volcado en la creación de nuevos archivos. De hecho, si el nombre del archivo coincide con uno existente (esto es, si tratamos de crear un archivo que ya existe), "x" provoca error, como sucede con este script (en el supuesto de que el archivo pruebaCrear.txt ya exista)

Para conocer el posicionamiento del puntero dentro del archivo disponemos de las funciones/métodos tell() y seek()

  • La primera (tell())es de escasa utilidad ya que devuelve la posición actual del puntero en forma de número opaco (que no se corresponde necesariamente con la posición en bytes). Por ello tell() es usado principalmente para marcar posiciones dentro del fichero.
  • La segunda (seek(desp:int,ref:int)) tiene mayor utilidad, por lo que interesa conocer su funcionamiento: esta función/método desplaza el puntero una serie de posiciones indicadas mediante un número entero (desp), tomando como referencia el valor de otro (ref) y que en el caso de los ficheros de texto toma dos valores: 0 para el inicio del archivo y 2 para su final. Esto condiciona, a su vez, los valores que puede tomar desp: 
    • Si ref=0, entonces desp puede ser 0 o un valor de posición obtenido mediante tell()
    • Si ref=2desp sólo puede tomar como valor 0.
Más interés tienen para nosotros los métodos o funciones asociados a la lectura de ficheros, la cual deriva del uso de "r" como complemento de la función open(), y que, como ya vimos en una entrada anterior, permite la apertura de un archivo en modo lectura. Estos métodos (o funciones) que ya hemos empleado son los siguientes:

  • read() Lee y devuelve caracteres desde la posición del puntero hasta el final del fichero, salvo que se especifique el número de caracteres a leer; en ese caso la expresión es como sigue: read(num_car:int):str.
  • readline() Lee y devuelve caracteres en forma de cadena hasta encontrar un salto de línea (\n), que también devuelve. El puntero se queda situado al final de la cadena leída. La fórmula completa de este método/función es el siguiente: readline(limite_car:int): str.
  • readlines() Lee y devuelve en forma de lista de cadenas las líneas del fichero desde la posición del puntero hasta el final. Si indicamos un límite (readlines(limite:int):list) leerá hasta la línea en que se encuentra ese límite, pero si el límite coincide con el final y cambio de línea, devolverá también la línea siguiente.
En este script te muestro las diferentes casuísticas de estos métodos. Menos la primera, el resto de las opciones están comentadas. Para activarlas deberás borrar el carácter # de cada una de ellas (no del comentario real, que va sangrado para que sea más fácil de identificar), comentando a su vez la formulación que estaba activa antes.