domingo, 15 de septiembre de 2024

LENGUAJE

Para empezar a trabajar con Python.

Incluso antes de empezar a estudiar los diferentes componentes de un lenguaje como Python, es necesario saber cómo podemos trabajar con este lenguaje. Este será el contenido de la presente entrada.


Empezaremos por el IDLE por su simplicidad y por ser el entorno de desarrollo que se instala con el propio lenguaje (1). Con él, y más concretamente con su Shell iremos comprobando el funcionamiento de las diferentes instrucciones, estructuras y funciones. También emplearemos el constructor de script que tiene asociado para crear script que podremos recuperar posteriormente (2). 

La forma más sencilla de trabajar con un script ya creado consiste en hacer doble clic en el icono con el que se identifica en su localización. Inmediatamente se lanza el cmd del sistema y se ejecuta automáticamente el script (3). El problema es que una vez ejecutado el script se cierra automáticamente el cmd. Si queremos evitar esto y el desagradable efecto que produce perder inmediatamente la información, deberemos añadir al final del script una instrucción que espere a ser ejecutada por el sistema; por ejemplo input() (4)

De estas formas tan simples, y sin necesidad de instalar programas específicos, podremos aprender a trabajar con Python, incluso a funcionar con los primeros programas que creemos con finalidades específicas y funcionales.

-----------------------------------------
LENGUAJE 
1 - 2 - 3 - 4 - 5
------------------------------------

NOTAS

(1) IDLE (abreviatura de Integrated Development and Learning Environment, en español: «Entorno de desarrollo y aprendizaje integrado»)​ es un entorno de desarrollo integrado para Python, que se incluye con la implementación predeterminada del lenguaje desde la versión 1.5.2b1. [Wikipedia]
(2) O interfaz con el sistema operativo. El shell es la capa más externa del sistema operativo. Un shell ofrece un método para comunicarse con el sistema operativo. Esta comunicación tiene lugar de forma interactiva (la entrada desde el teclado se ejecuta inmediatamente) o como un script de shell . Un script de shell se crea mediante un soporte tipo procesador de texto, aunque puede resultar más adecuado llamarlo constructor de script, por la especificidad de su función. La diferencia entre trabajar con el Shell y con el constructor es que con el primero no podemos guardar el script, pero sí con el segundo, ya que este permite almacenar el script en un archivo. [Ver en este enlace]. Para crear script también podemos utilizar un procesador de texto sencillo, como Bloc de notas, pero siempre deberemos guardar el archivo creado con extensión .py.
(3) El cmd o símbolo del sistema es el intérprete de comandos en OS/2 y sistemas basados en Windows NT. Es el equivalente de COMMAND.COM en MS-DOS y sistemas de la familia Windows 8.1x [Wikipedia]
(4) Por ejemplo, input("Pulsa ENTER para finalizar") hace que el cmd mantenga activa su ejecución y visualización hasta que pulsemos la tecla ENTER (y no otra), ya que, como sabemos, es ésta la que pone fin a la ejecución de la función input()

LENGUAJE.

Componentes básicos.

Todo lenguaje cuenta con una serie de componentes básicos que son los que permite desarrollar los procesos fundamentales de que consta el desarrollo de un algoritmo y que, en términos generales, se ajusta al conocido esquema entrada-procesamiento-salida.


En síntesis la programación o computación (y de ahí le viene el nombre) consiste en la ejecución de cómputos sobre datos para la obtención de unos resultados, sean éstos los que sean. Esto, que es otra forma de formular el esquema anterior, supone y pone en evidencia la importancia de disponer de datos o valores, bien sean que los incorporemos como parte del programa, o bien que cedamos al usuario (en parte) su introducción mediante algún tipo de interfaz de entrada. 

Pero lo que ahora nos interesa es constatar que esos datos se almacenan en espacios de memoria RAM y que para acceder a ellos necesitamos alguna forma de referenciarlos: este es el papel de las variables, que no son otra cosa que referencias a los datos, referencias conocidas por explicitadas, claro está. 

Al contrario de lo que el lenguaje natural da a entender, en realidad ni las variables contienen datos (no son espacios de memoria, sino referencia, identificadores o "nombres" de esos espacios de memoria) ni la dirección de la relación es var -> dato. En realidad, la relación es a la inversa var <- dato, y así se representa la referenciación del dato a la variables en los pseudolenguajes con que se expresan los algoritmos (1).

Los datos pueden ser de diferentes tipos, siendo los más destacados los alfanuméricos (caracteres y sus derivados, las cadenas de texto o string), los numéricos, en sus diferentes tipos (enteros y decimales) y los booleanos.

Como trabajar con variables individuales puede resultar poco eficiente, los datos se pueden organizar en colecciones. Estas colecciones reciben diferentes nombres y tienen distintas características y funcionalidades (no siempre equivalentes entre los diferentes lenguajes), pero podemos identificarlas grosso modo y en términos generales con lo que normalmente se denominan como matrices.

La segunda parte del significado de la computación es la realización de cómputos u operaciones sobre los datos. Esta fase es tan importante que ocupa el centro del esquema básico (Procesamiento), siendo su componente fundamental.  Estas operaciones dan nombre y sirven para definir al conjunto de instrucciones que llamamos operadores y que se diferencian en función de los datos con los que tienen que trabajar: operadores de string, operadores algebraicos y operadores lógicos son sus categorizaciones más comunes.

Aunque la forma de procesamiento de los datos suele ser predominantemente lineal y jerárquica, lo que define la programación como un proceso basado en la sucesión de instrucciones, existen dos estructuras fundamentales y complementarias que responden a dos principios: el de bifurcación y el de iteración: el primero se concreta en las estructuras condicionales en las que se basa la opcionalidad, y el segundo como estructuras cíclicas o bucles, que permiten la repetición de instrucciones n veces y/o condicionadas al cumplimiento de un criterio. La conjunción de estos tres principios: linealidad o sucesión, bifurcación e iteración, conforman el cuerpo de un script. Aunque no necesariamente tienen que estar las tres presentes, raro es el algoritmo de cierta complejidad que no las requiere en cierta medida.

Tan raro como lo es el algoritmo que se pueda desarrollar (2) recurriendo únicamente a una lógica de programación basada en un único script. Salvo los problemas simples, la mayoría requieren secuencias largas de operaciones y el uso de diferentes estructuras (incluyendo su combinación y anidamiento). Cuando esto se observa y especialmente cuando segmentos de código se repiten con cierta frecuencia es cuando tenemos que pensar en las ventajas de la programación modular, cuya expresión más simple es la definición de funciones (3). 

Cuando predomina el uso de funciones y su combinación sobre el código lineal, hablamos del paradigma funcional y cuando el procedimiento de trabajo se basa en definir objetos (clases de objetos) formados por características y funciones, entonces nos estamos situando en el paradigma de la programación orientada a objetos (POO).

Python se define como lenguaje basado en la POO (4), con Python podemos trabajar siguiendo el paradigma imperativo-lineal y también podemos crear funciones, así que se comporta como un lenguaje que nos permite aprender diferentes formas de trabajar y de afrontar la solución de los problemas que nos planteemos. 

Junto con su simplicidad (relativa) como lenguaje, esa flexibilidad en términos de formas de trabajo hace del lenguaje Python una buena herramienta para el aprendizaje; pero lo que es más importante: también para la resolución de los problemas específicos relativos a nuestro trabajo para los que nos planteemos desarrollar soluciones informáticas.

-----------------------------------------
LENGUAJE 
1 - 2 - 3 - 4 - 5
------------------------------------

NOTAS

(1) Por ejemplo en [PSeInt], una excelente herramienta para el aprendizaje de la lógica de programación que recomiendo conocer (y emplear) para este objetivo. Además de trabajar con pseudocódigo y simular el funcionamiento del algoritmo, también dispone de herramientas de representación gráfica o diagramas de flujo. Tienes referencias a PSeInt más detalladas en [Anexos].
(2) Al menos de forma eficiente.
(3) En algunos lenguajes (y también planteamientos teóricos) se diferencia entre subrutinas y funciones. En Python se mantiene la misma denominación para ambas, aunque se diferencias las funciones que devuelven datos de las que no.
(4) De hecho muchos de sus componentes no son otra cosa que objetos, incluyendo los tipos de datos, como la clase string, lo que dota de gran potencia y versatilidad al trabajo con los datos ya que, en tanto objetos, tiene propiedades y métodos.

viernes, 13 de septiembre de 2024

TEXTOS. Cadenas.

Variables alfanuméricas o cadenas.

Inicio esta sección del blog no con el análisis del elemento más básico, que es el carácter, pero sí con el que resulta ser el mínimo funcional para el trabajo con textos mediante Python: las cadenas alfanuméricas o string.


Como ya vimos en [esta entrada] (pendiente), las cadenas alfanuméricas son uno de los tipos de datos que podemos manejar con el lenguaje Python, y el fundamental para lo que en esta sección [TEXTOS] nos ocupa (las distintas forma de manejar textos) y nos interesa (automatizar la creación de textos) en función del interés que tiene para el trabajo del orientador.

Empezaré por tratar con más detalle sobre las cadenas de caracteres (string, de ahora en adelante), tanto por su carácter básico como por el estatus que en realidad tiene que posiciona al string en una zona intermedia entre el dato y la colección de datos. Esto incrementa la complejidad de tratamiento, pero también ofrece un amplio conjunto de posibilidades de manejo en Python (1).

Ya sabemos por lo visto en la sección [LENGUAJE] que, al contrario que en otros lenguajes, en Python no es necesario declarar previamente de qué tipo es una variable (2), siendo suficiente con asignarle contenido para que quede realizado el tipado, que, además, se modificará en función de los cambios de contenido que pueda recibir la variable a lo largo del programa. En consecuencia, la asignación de contenido string a una variable en Python es tan sencillo como esto: VarSaludo = "Hola Mundo". Y es igual de sencillo para contenidos mínimos (un carácter) que máximos (un texto completo).

Precisamente por ello, disponemos en Python de determinadas funciones que nos permiten conocer de qué tipo es cada variable (3), una de ellas es la función type() (4). Usando esta función podemos apreciar el significado de Python como lenguaje de tipado dinámico:

var1 = "1"
print(type(var1))
<class 'str'>

Inicialmente asigno a var1 el dato alfanumérico "1" (var1 = "1") y mediante la función type() (print(type(var1))) obtengo información respecto a su categorización como string (<class 'str'>), pero si modifico el dato asignado a var1 (ahora como valor numérico -> var1 = 1), lo que me devuelve la type() es que la variable es (pertenece a la clase de tipo) integer (<class 'int'>).

En esta primera entrada me limitaré a tratar dos de los procesos implicados en el desarrollo de un algoritmo: la entrada o input y la salida o output. El procesamiento, que es la parte más compleja, requerirá diversas entradas, aunque la primera y más básica (al menos para lo que a nuestros objetivos ser refiere) es el encadenamiento de string, por lo que trataremos esta cuestión en breve.

Para en entrada (input) de datos, en Python empleamos la función input() (5), con la que obtendremos resultados diferentes en función del modo (directo o indirecto) en que usemos dicha función. Si la usamos directamente (input ("Nombre: ")) ...

input ("Nombre: ")
             
Nombre: Javier
'Javier'

... en el modo directo (desde el Shell) se producirá la ejecución de la instrucción, a la que responderemos (Nombre: Javier) y obtendremos como resultado nuestra respuesta entrecomillada ('Javier'), clara indicación de que el "programa" nos está devolviendo un dato de tipo string.

Pero si la empleamos indirectamente, esto es, asociada a una variable (vsNombre = input("Nombre: " )), también en el Shell, obtendremos como respuesta de ejecución la petición realizada (Nombre:), quedando el programa a la espera de nuestra respuesta (igual que antes), pero a diferencia del procedimiento anterior, una vez que respondemos a la petición de entrada de datos, el programa finaliza o queda a la espera de las siguientes instrucciones, esto es, no produce ninguna salida informativa como sí hizo antes.

Es este el momento en el que entra en acción la función output (salida) print() (print (vsNombre)), que produce aparentemente la misma salida, pero con una diferencia: ahora el string de respuesta no aparece entrecomillado (Javier). Esta pequeña gran diferencia está informando de que la respuesta no es informativa del sistema, es la continuación del programa mostrando la salida esperada en función del procesamiento del dato entrante que previamente hemos suministrado, procesamiento que ha realizado la función print().

Digo pequeña diferencia porque únicamente aparenta ser una cuestión de forma (con vs. sin comillas), pero en realidad es una diferencia grande, dado que afecta a la lógica del funcionamiento del código: quedar a la espera de una nueva instrucción implica solicitar continuidad del procesamiento, que en este caso se concreta como función de salida (print()) (6). Podemos considerar la combinación input() - variable - print() como la base del proceso de programación, esto es, de generación de un procedimiento basado en un algoritmo.

Otras formas de construir estos pequeños "programas" (7), empleando únicamente ambas funciones, es informar primero (mediante print()) de la demanda que sigue, y que se canaliza a través de input(), para devolver finalmente el resultado mediante (de nuevo) la función print():

print("Por favor, dime tu nombre:")

Por favor, dime tu nombre:

vsNombre = input()

Javier

print(vsNombre)

Javier

También podemos hacer uso del argumento end de la función print(), evitando así el salto de línea en el momento de la ejecución del programa (print("Por favor, dime tu nombre:"), end="")

De estas diferentes formas de trabajar con los procesos de entrada y salida, el más común es el primero, pero todos ellos no dejan de ser variaciones sobre el mismo procedimiento de escaso interés práctico; lo importante es que sobre ellas construiremos el trabajo con los textos, según iremos viendo en las entradas que siguen en esta sección.

NOTAS

(1) Un string es ciertamente un dato (una unidad de información) que podemos manejar como tal mediante su asignación o referenciación a una variable, pero también una colección de caracteres. Siendo como es el carácter el elemento mínimo de información del string, este se comporta (y se maneja) como colección de datos, más concretamente como lista, de ahí que en Python sea posible trabajar con el string como con una lista. En realidad, en términos de modelo de objeto, el string es una clase de objeto, por lo que en el trabajo con él podemos hacer uso de los métodos y los atributos que tiene asociados. De ahí la riqueza y la complejidad de presenta Python en el manejo de este tipo de dato.
(2) Recuerda lo explicado [en esta entrada] (pendiente) respecto a Python como lenguaje de tipado dinámico: la variable es del tipo del dato que se le asigne. En otros lenguajes, como en OOo Basic, podemos (y es conveniente) determinar a priori el tipo de dato que contendrá una variable antes de realizar la asignación (Dim sNombre As String) como paso previo para sNombre = "Javier"). Esta exigencia, al menos en algunos lenguajes, resulta conveniente, ya que evita errores de programación cuando el script es de cierta complejidad y extensión.
(3) Ya sabemos que lo correcto es decir qué tipo de contenido o dato está referenciado por una variable, ya que la variable no es otra cosa que un identificador, una clave, que facilita al lenguaje el acceso a una posición de memoria RAM en la que se almacena el dato; en este caso nuestro string. Pero, a riesgo de simplificar, en este blog emplearé expresiones como la anterior, ya que resulta más sencillo y se supone al lector el conocimiento teórico básico suficiente para no dar lugar a una confusión que, por otra parte, es poco relevante en términos operativos.
(4) Desde [este enlace] puedes acceder a una explicación más detallada sobre funciones y formas de conocer el tipo variable. En relación con la naturaleza de objeto que tienen los tipos de datos (entre ellos los string) es interesante la información que se aporta el enlace anterior respecto a las funciones dir() y __class__, que permiten conocer los atributos de cada clase-tipo de dato.
(5) input() devuelve siempre y por defecto un string, por lo que si trabajamos con datos de otro tipo deberemos convertirlos al tipo mediante las funciones correspondientes. Aquí, como estamos trabajando con string, esa conversión no es necesaria.
(6) El Shell también nos permite comprobar el funcionamiento de print() como función pura, siendo esta precisamente la forma básica y prototípica en que suele empezar todo aprendizaje de un lenguaje de programación (print("Hola mundo") -> Hola mundo). Obsérvese como la respuesta no está entrecomillada.
(7) Esto y lo que sigue tiene sentido más como parte del aprendizaje formal del lenguaje que utilidad práctica (en este sentido sólo aporta variaciones sobre el mismo tema) y es más propio de trabajo sobre la utilidad de escritura de código del IDLE que para su implementación directa en el Shell.


jueves, 12 de septiembre de 2024

LENGUAJE

Lógica de programación

Parto de que la alfabetización digital, ahora entendida como competencia digital, que no es lo mismo pero se parece y para qué andarnos con diferenciaciones de matiz, tiene como uno de sus (discutidos) componentes, la lógica de programación. Digo discutidos porque, en sentido estricto, no es necesario dominar dicha lógica para ser un usuario más o menos competente de los recursos digitales (competencia digital a nivel usuario, que es la realmente predominante), pero sí en fundamental para hablar de alfabetización digital, también en sentido estricto, que asimilo a lo que el conocimiento y dominio de la lectura y la escritura son a la alfabetización a secas: conocer y saber usar el código, comprender los mensajes y componer textos. En esas diferencias radican las que se dan entre determinada forma de entender la competencia digital y el significado real de lo que supone la alfabetización digital. Evidentemente defiendo la necesidad de lograr esta última y considero falaz un desarrollo competencial que nos limite al mero papel de usuarios de tecnologías y algoritmos comercializados, sin negar la evidencia de que su conocimiento y dominio de uso son también necesarios. Necesarios sí, pero no suficientes.


Podemos definir como lógica de programación la formalización mediante representaciones y lenguajes formales de la capacidad de identificar un problema y organizar y estructurar ideas e instrucciones de forma coherente para su resolución. Esta estructuración que deriva y se expresa en lo que llamamos algoritmo, se realiza mediante la aplicación de reglas y procesos coherentes.

De la definición anterior se siguen dos cuestiones principales que revelan su importancia en el proceso de alfabetización que pretendemos:
  • Que existe y se conoce un problema, luego hay un conocimiento "experto" previo y una situación real que se problematiza.
  • Que se procede mediante un lenguaje formalizado, dotado de forma y regido por una lógica propia, no siempre equivalente (al menos en lo formal) a otros lenguajes y a otras lógicas. Un lenguaje que, en consecuencia, es necesario comprender y "hablar".
Si de la primera se deriva la importancia del conocimiento "experto" del problema y de su contexto (lo cual es clave en un enfoque instrumental y aplicado de la alfabetización digital), la segunda implica que existe una diferencia entre el conocimiento no formal de la algoritmia "natural", que la que aprendemos en función de nuestra experiencia vital socializada y la alfabetización digital, que supone el conocimiento de un lenguaje básico formalizado (que generalmente denominamos pseudolenguaje) mediante el que se expresa el algoritmo, y la formalización de la representación (gráfica) del mismo mediante diagramas de flujo.

Aunque este conocimiento básico subyace a todo proceso de alfabetización en lógica de programación, no es hasta que se conoce y "habla" un lenguaje real de programación que se alcanza la capacidad de aplicar la lógica de programación a la resolución de un problema, siendo éste el objetivo y la medida del logro del proceso de alfabetización. Es aquí donde se revela necesario optar por un lenguaje en función de los objetivos reales que se planteen alcanzar. Dicho de otro modo, es en este momento donde debemos valorar la idoneidad de Python como lenguaje de programación en el que basar el proceso de alfabetización digital.

-----------------------------------------
LENGUAJE 
1 - 2 - 3 - 4 - 5
------------------------------------

LENGUAJE.

¿Por qué Python para orientadores?

Responder a esta pregunta requiere plantearse previamente otra más básica: ¿ qué aporta saber lógica de programación a un orientador?. Tampoco esta pregunta tiene fácil respuesta, pero es básica y pertinente.


No considero una exageración decir que la lógica de programación (y lo que supone en concreto: saber programar) es la alfabetización pendiente del siglo XXI. También lo era a finales del siglo XX, y quedó como tarea pendiente. Era de esperar.

Hicieron falta casi 7000 años para que la alfabetización a secas pasara a ser considerada una necesidad social universal; mucho menos para que el grado de alfabetización de la población (y no sólo de una minoría) fuera un factor relevante para el desarrollo de las comunidades; y que además fuera posible, gracias a la invención de la imprenta y la subsiguiente generalización del libro como recurso accesible (allá por el siglo XV d.C.) (1).

El desarrollo de la informática, de lo digital como soporte y medio omnipresente se ha impuesto como realidad en muy poco tiempo, modificando (y no necesariamente para bien; tampoco su contrario) el modo en que se plantea la comunicación, el acceso a y la transmisión del conocimiento. Por debajo de este fenómeno y todo su instrumental tecnológico está la electrónica, pero sobre todo la lógica de programación (2).

Desconocer esta base es quedar indefenso a sus consecuencias, aunque no afecta necesariamente a la capacidad de hacer uso de las tecnologías en que se manifiesta (que son muchas). Y este es, en mi opinión, un gran y potencial (cada vez más real que potencial) riesgo; especialmente ahora, cuando el propio desarrollo de esas tecnologías amenazan con competir (y con ventaja) con la propia inteligencia humana. El sólo hecho de que sea necesario calificar la inteligencia como humana (por oposición a la inteligencia artificial) ya es en sí mismo todo un indicio (mucho más que un indicio) de cómo están las cosas (3).

También lo es que podamos hablar de alfabetización digital como símil, pero también como diferenciación de la alfabetización a secas. No sé si es muy apropiado emplear este término, por lo que conlleva de comparación con el original, pero por algo es que más empleado: si ambas alfabetizaciones no son equivalentes (y hay muchos matices específicos en la digital para que no lo sean en sentido estricto), lo parecen, principalmente por sus repercusiones, aunque es posible que aun no podamos apreciar que el analfabetismo digital pueda tener aun mayor transcendencia que el analfabetismo a secas. Y no podemos descartar que, gracias a la IA, cuando sea una realidad evidente haya quedado oculta, que no el lo mismo que sea irrelevante (4).

Tampoco descarto que este discurso nos esté conduciendo a territorios un tanto alejados de aquello que le dio origen; pero creo que era necesario llegar hasta aquí (posiblemente no no sea ir más lejos) para sentar las bases de la respuesta a esa segunda pregunta. La primera, en pura lógica, queda aun pendiente.

Retomando la cuestión, no podemos resumir la competencia digital al conocimiento de la lógica de programación, ya que aquella es mucho más amplia, multifacética y compleja que la lógica de programación, y en esos términos generales, variopintos y multifacéticos es posible que estemos más cerca de ser competentes, o al menos suficientemente competentes, aunque tampoco hay que tirar voladores (la brecha digital se ha mostrado como una manifestación relevante de la brecha social, y también lo es en términos de grupos etarios).

Lo malo es que hay formas y niveles de alfabetización digital que son claramente insuficientes y nos sitúan, en el mejor de los casos, como meros usuarios-consumidores de contenidos y medios, a la vez que nos alejan de la posibilidad de ser realmente competentes (ergo usuarios capaces de comprender la lógica del código y acceder critica y autónomamente al contenido). Es como si limitáramos la alfabetización a la capacidad lectora, olvidándonos de su otra parte: la de ser capaces de escribir.

Y no es esta una buena comparación, aunque puede que sí la más sencilla de comprender, la que mejor ilustra la cuestión. En realidad, carecer de competencia en lógica de programación puede ser aun más limitante que no saber escribir, ya que esa forma concreta de analfabetismo digital nos limita mucho más dado que  afecta también a nuestra capacidad de comprender el código y la tecnología que le da soporte. Podemos ser hasta hábiles usuarios de la máquina y competentes consumidores de programas y contenidos, pero ignorar prácticamente todo de lo que subyace y lo hace posible; es más: ni siquiera esa alfabetización aparenta ser necesaria. Y sí lo es.

Es necesaria para seguir siendo capaces de tomar decisiones, para evitar que unos pocos las tomen por todos. Es necesaria para comprender la lógica que subyace a una tecnología cada vez más omnipresente y día a día más potente... Y para otros muchos "para..." (5)

Pero también lo es para algo más modesto y concreto: para servirnos de ella de forma más competente (no sólo más productiva) y adaptarla a nuestras necesidades concretas, especialmente las que derivan del desempeño profesional. Esto es una necesidad general y compartida, pero especialmente relevante cuando el colectivo profesional es suficientemente minoritario y hasta marginal para el sistema como para resultar rentable en términos empresariales. Nuestro colectivo encaja dentro de este "privilegiado" grupo.

¿Y por qué Python?.

Cierto que de lo anterior no se deriva más que la necesidad de conocer y saber usar la lógica de programación (dentro del contexto de la alfabetización y la competencia digital), pero no se concreta necesariamente en ningún lenguaje específico. Es más, en términos concretos e inmediatos, en esos que se orientan a dar respuesta a nuestras necesidades profesionales específicas (precisamente por su concreción y especificidad), es posible que otros lenguajes y otros enfoques de concreción competencial se ajusten más y resulten de mas inmediata utilidad (6).

No se puede negar la lógica de lo anterior, como tampoco que esas otras soluciones son tan alfabetizadoras y tan capaces como Python de sustentar el desarrollo de ese aspecto nuclear de la competencia digital que hemos llamado conocimiento de la lógica de programación. Como Python o como cualquier otro lenguaje de programación.

No obstante, hoy por hoy, hay algo en Python (y no necesariamente en otro lenguaje de programación, pero tampoco tan exclusivamente en Python), que hace que sea cuanto menos tentador y conveniente incluir el conocimiento de este lenguaje como herramienta para concretar el desarrollo de la competencia digital en su concreción como lógica de programación: el peso de este lenguaje en la consolidación de las nuevas (ya no tanto) tendencias en programación (incluyendo el desarrollo de la mal llamada IA).

Python nos permite dar respuesta a las mismas necesidades que podemos cubrir con un lenguaje de macros (script, prefiero decir), y nos abre muchas otras posibilidades, con independencia de que nos limitemos a vislumbrarlas (y estar en posición de entenderlas), pretendamos incorporarlas críticamente en nuestro que hacer o nos atrevamos a acercarnos a la producción de recursos. Esta amplitud de opciones y campos de estudio no nos las proporcionan los lenguajes encerrados en suites ofimáticas, aunque sean más que suficientes para fines generales competenciales y adecuados para dar respuesta a necesidades muy concretas.

Python no ofrece dar respuesta a lo básico, concreto y necesario y nos abre un mundo de posibilidades; muchas más de las que vamos a ser capaces de convertir en realidades. Y aunque puede que ni falta que hace recorrer estos caminos, al menos algunos de ellos y con cierta profundidad, la mera posibilidad de comprender en qué consisten y cómo se concretan ya es una aportación de gran valor para eso que llamamos alfabetización digital. 

Aplicando de nuevo el símil de la alfabetización, además ayudarnos a aprender a leer y a escribir en sus dimensiones más funcionales, Python potencia en gran medida nuestra capacidad de comprensión lectora. No hace falta que seamos capaces de crear obras como La divina comedia o Crítica de la razón práctica, pero sí que seamos capaces de leerlas, comprenderlas (en cierta medida, al menos) y disfrutarlas.

-----------------------------------------
LENGUAJE 
1 - 2 - 3 - 4 - 5
------------------------------------

martes, 10 de septiembre de 2024

Python Lenguaje

Funciones para segmentar cadenas

Siguiendo con el interesante mundo de las cadenas en Python, después de la revisión de opciones de concatenación que hice en una entrada anterior, deseo en ésta revisar las principales funciones de que disponemos para manejar y formatear el contenido de las cadenas. La importancia de estos contenidos deriva de la propia del uso de cadenas en la automatización de documentos.


Enumero aquí las principales funciones, ejemplificando su modo de uso, empezando por len().

La función o método len() permite conocer el número de caracteres que contiene una cadena; esto es, devuelve el entero de dicho valor,. En este recuento incluye los espacios que separan las palabras (en caso de tratarse de un string con varias palabras)  y los signos de puntuación, que son considerados por len() también como caracteres.

print(len('Hola'))        -> 4
print(len('Ho la'))       -> 5 
print(len('¡Hola!'))      -> 6

Este método (len()) también evalúa variables que referencian string

print(len('Maria')              -> 5

nom = 'Maria'
print(len(nom))                 -> 5

y admite concatenación de literales y variables

cad1 = 'Los verdes prados de Oviedo'
print('La cadena ' , cad1 , ' tiene ' , len(cad1), ' caracteres')

así como diferentes procedimientos de concatenación

print(f'La cadena {cad1} tiene {len(cad1)} caracteres')
print('La cadena {} tiene {} caracteres'.format(cad1,len(cad1)))

Si len() permite identificar el número de elementos de una cadena es porque una cadena se comporta como una lista de caracteres, motivo por el que cada carácter tiene asociado un índice en el string mediante el cual es posible acceder al carácter. Así, por ejemplo, para cad1

print(cad1[0])         -> L (primer elemento del string asignado a cad1)
print(cad1[-1])        -> o (último elemento del string asignado a cad1)

NOTA 1 -> Obsérvese  que, en orden directo, la matriz de elementos se inicia en 0 y que los índices negativos permiten acceder a los elementos de la cadena en orden inverso. 

NOTA 2 -> Esta cualidad de la cadena como lista de caracteres y el uso de funciones de formato pueden ser de gran interés para la manipulación de segmentos de cadena. Tendremos ocasión de verlo con más detalle en una entrada que complemente a esta.

Ahora ya sabemos que una cadena se puede descomponer en una lista de caracteres, que la función len() nos permite conocer cuántos son estos y que podemos acceder a cada uno de ellos solicitándolos por su índice. 

Estos conocimientos son muy interesantes y es posible darles utilidad en el desarrollo de algoritmos de automatización de documentos. Pero no son las únicas utilidades que pueden tener utilidad en este ámbito de uso... ni en otros. Me estoy refiriendo en concreto a la utilidad que pueden tener dos métodos de trabajo con los componentes de una cadena: split() y join() 

Método split() permite identificar los segmentos de una cadena, no los caracteres que la componen, sino las subcadenas en que se divide. Su sintaxis básica es la siguiente:

cadena.split(sep,maxsplit)    donde...

    • cadena es la cadena o variable string sobre la que se aplica split()
    • sep es el argumento que identifica el carácter por el que se divide la cadena. Por defecto es el espacio (' ').
    • mxsplit es el argumento que establece el número de veces en que deseamos dividir la cadena (o número de apariciones de sep que deseamos utilizar como referente para separar las subcadenas). Por defecto su valor es -1, que indica que deseamos utilizar todas las apariciones del carácter sep  

Veamos algunos ejemplos utilizando cad1="Los verdes prados de Oviedo" 

divide_cadena = cad1.split()
print(divide_cadena)                  (1)  -> ['Los', 'verdes', 'prados', 'de', 'Oviedo']
divide_cadena = cad1.split(' ',2)
print(divide_cadena)                  (2)  -> ['Los', 'verdes', 'prados de Oviedo'] 
divide_cadena = cad1.split('d',-1)
print(divide_cadena)                  (3)  -> ['Los ver', 'es' pra', 'os de Ovie', 'o']

  • La formulación 'por defecto'  (1) divide la cadena en las palabras que forma la frase.
  • Cuando modificamos el parámetro mxsplit (2) obtenemos un número diferentes de secuencias. Concretamente 2 divide la cadena en tres partes: las resultantes de las dos primeras apariciones de sep (' ') ['los'] y ['verdes']  y el resto de la cadena ['prados de Oviedo']
  • Si utilizamos otro criterio para sep (en 3 -> 'd'), manteniendo mxsplit por defecto (-1), obtenemos segmentos identificados en función de la aparición del carácter sep

Por eso dije subcadenas y no palabras, aunque es posible que, al menos inicialmente el uso más frecuente sea el establecido por defecto, esto es, la creación de la lista de las palabras que forman la frase.

El resultado 'normal' del método split() nos da acceso a una lista que va a sernos de mucha utilidad cuando queramos modificar el contenido de esa lista. Utilizaremos para ello lo que sabemos respecto al manejo de listas: acceso a elementos, sustracción, adición y sustitución de elementos... Pero es posible que esto sea tema para tratar en otra entrada, así que mejor completamos la actual hablando del método join()

El método join() sirve para invertir los efectos del método split(), esto es, para unir los elementos diferenciados de una lista en una única cadena. Veamos cómo:

divide_cadena = cad1.split()             1-> Divide la cadena cad1 y asigna divide_cadena
unir_cadena=' '.join(divide_cadena)  2-> Une divide_cadena y asigna a unir_cadena
print(divide_cadena)                        3-> ['Los', 'verdes', 'prados', 'de', 'Oviedo']
print(unir_cadena)                           4-> Los verdes prados de Oviedo

Si en vez de 2 escribimos el método join() precedido de '__' , obtendremos en 4 el siguientes resultado: Los__verdes__prados__de__Oviedo.

TEXTOS. Cadenas.

Concatenar cadenas

No es esta la primera vez que hablo de cadenas y de las formas de concatenación disponibles en Python, pero la revisión de lo hecho y el estudio del tema me han convencido de la necesidad de tratar de nuevo esta cuestión, profundizando más en ella y analizando con cierto detalle los diferentes modos en que se planea este procedimiento de generación de código. La importancia que tiene en procesos de automatización documental justifica sobradamente que preste especial atención al tema.


Tampoco es sobre Python el único lenguaje respecto al cual trato el tema de la concatenación, ya que también dediqué alguna que otra entrada para tratar el tema tanto respecto a las funciones nativas de LibreOffice-Calc como en el lenguaje OOo Basic. De hecho, si vuelvo ahora sobre esta cuestión en Python es precisamente a consecuencia de haber trabajado sobre el mismo tema tomando como referencia el servicio LO-Calc.

Es posible que, al retomar el tema, se produzcan repeticiones de contenido que tal vez depure en una posterior revisión del conjunto de las entradas, pero que ahora sólo puedo lamentar como consecuencia lógica del paso del tiempo y de la complejidad de la temática. Te ruego disculpas, lector, por la molestia que esta repetición te pueda suponer.

Como vimos, en OOo Basic se utilizan dos formas básicas y se puede decir que intercambiables de concatenar cadenas: mediante + y & podemos concatenar tantos segmentos de texto y/o variables como deseemos, sin más límite que la extensión del párrafo que queramos crear y la inteligibilidad del código resultante, a cual, al ser un proceso repetitivo puede ser poco elegante, pero no necesariamente disfuncional o ininteligible.

Recuerdo que en su momento recomendé utilizar en OOo Basic & para concatenar string (y variables string) y reservar + para la operación de sumar, como forma de facilitar la lectura del código, pero se trata de una opción y preferencia personal, sin que exista (que yo sepa) motivación técnica específica para defender esta opción.

Vimos en una entrada anterior que en Python también podemos concatenar cadenas de forma directa mediante , y +, pero presentan limitaciones y restricciones que no se observan en OOo Basic, por lo que son fórmulas no recomendables en cuanto la combinación de cadenas adquiere cierto grado de complejidad, siendo para ello suficiente con que queramos combinar string literal con variables.

Dado que Python es un lenguaje en desarrollo, las versiones que se han ido creando con el paso del tiempo han ido incorporando soluciones diferentes y dejando atrás como obsoletas las que se mostraron limitadas, aunque no por ello dejan de estar disponibles, como pasa con los modelos básicos anteriores.

El último desarrollo en estas funcionalidades son las llamadas cadenas f, de las cuales ya te hablé en esta entrada, pero es posible que dejara por el camino procedimientos interesantes y aun vigentes que surgieron en su momento como respuesta a las demandas del procedimiento. Trataré en esta entrada de tapar estos huecos.

Todo esto es para confirmar que el tema de la concatenación de cadenas ya ha sido tratado en este blog, pero, una vez dicho, paso a indicar que, lo que sigue constituye una reformulación sistemática y ampliada de lo que esas entradas pueden haber recogido, a la vez que una continuidad y conclusión de lo explicado respecto a LibreOffice y OOo Basic en otro blog.

Así que paso a exponer paso a paso el análisis de las diferentes formas de concatenar cadenas en Python. Tomo como referencia lo que expone Alfredo Sánchez Alberca en su material on-line Aprendiendo con Alf, al que remito y del que me declaro deudor.

Expongo en primer lugar diferentes formas de uso de conectores [, y +], que son las formas equivalentes a lo que en OOo Basic son [& y +] respectivamente.

Primera formulación. Concatenación de dos literales con separación de coma (,).

print("Hola","Javier") -> Hola Javier

Segunda formulación. Concatenación de literal y variable con separación de coma (,).

nom = "Javier"
print("Hola",nom) -> Hola Javier

Tercera formulación. Concatenación de dos variables con separación de coma (,).

sal = "Hola" 
nom = "Javier"
print("Hola",nom) -> Hola Javier

Cuarta formulación. Concatenación de dos literales con separación de signo (+).

print("Hola"+"Javier") -> HolaJavier

Quinta formulación. Concatenación de literal y variable con separación de signo (+).

print("Hola"+nom) -> HolaJavier

Sexta formulación. Concatenación de dos variable con separación de signo (+).

print(sal+nom) -> HolaJavier

NOTA 1 -> Ambas fórmulas funcionan igual (salvo en determinados casos) siendo los contenidos string, con la única diferencia que el uso de coma (,) genera un espacio de separación entre los componentes, mientras que el uso del signo más (+) no lo genera. Este caso es necesario implementarlo como parte del literal o como cadena formada por un espacio (' ' o " ") y posicionada entre los demás componentes.

Séptima formulación. Concatenación de literal string y literal numeral con coma (,)

print("Número",123) -> Número 123

 Octava formulación. Concatenación de literal string y literal numeral con signo (+)

print("Número"+123) -> [TypeError: can only concatenate str (not "int") to str]

print("Número" + str(123)) -> Número 123

NOTA 2 -> Uno de los casos en los que (,) y (+) no funcionan igual es cuando queremos combinar texto y números dentro de la concatenación. En ese caso mientras que podemos hacerlo sin problemas si usamos (,) (como en Séptima), no funciona si usamos (+) (como en Octava). En ésta opción debemos convertir el número antes a string mediante la función str().

En segundo lugar vamos a explicar el uso de la primera fórmula de formateo de cadenas que se empleó en Python desde sus primeras versiones como lenguaje de programación. Se base en el uso del signo %s, siendo % el operador que hace de marcador y la referencia a la cadena que se anexa.  %s que se sitúa necesariamente dentro de la cadena en la posición en la que se desea ubicar el segundo segmento de la cadena y se puede utilizar tantas veces como sea necesario en combinaciones complejas. En estos casos la concatenación utiliza también los signos de las configuraciones ya vistas, especialmente el signo [,]. 

Al igual que los concatenadores [, y +] presenta también varias formulaciones, que enumeraré siguiendo el orden ya establecido.

Novena formulación. Literal cadena con unión de segundo elemento (cadena).

print('Hola, me llamo %s' % 'Javier') -> Hola, me llamo Javier

Décima formulación. Literal cadena con unión de segundo elemento (número).

print('Número %s' % 123) -> Número 123 

Undécima formulación. Literal cadena con unión de variable.

nom = 'Javier'
print('Hola, me llamo %s' % nom) -> Hola, me llamo Javier 

NOTA 3 -> En los tres casos el funcionamiento el correcto y el uso y posicionamiento del %s sigue el mismo patrón (se sitúa al final del primer literal), lo es debido al tipo simple de concatenación, pero no responde a una exigencia de la sintaxis de código.

Décimo segunda formulación.  Fórmulas mixtas y complejas.

A. print('Hola %s' % nom,',buenos días') -> Hola Javier, buenos días

B. print('Hola %s','buenos días' %nom) -> TypeError: not all arguments converted during string formatting

C. print('Hola %s', buenos días', %nom) -> Hola Javier, buenos días

D. print('Hola %s' % nom,',buenos días') -> Hola Javier, buenos días

E. annos = 24
    print('Hola %s' % nombre, ', tienes %s' % annos , 'de edad (eso quisieras)') -> Hola      Javier, tienes 24 años de edad (eso quisieras)

F. print('Hola %s, tienes %s' % nom % annos, 'de edad (eso quisieras)') ->TypeError:       not enough arguments for format string

NOTA 4 -> Lo que revela esta formulación (y sus variantes) es que es posible concatenar más de una cadena (o componente, ya que también es válido para números y para variables) siempre que se respete la sintaxis de %s que obliga a:

  • Posicionar el marcador en la posición requerida por la formulación deseada de la cadena resultante de la concatenación (A)
  • Uso de tantos marcadores como sea necesario en función de lo deseado (C, E)
  • Pero con restricción de posicionamiento: cada uso de %s debe posicionarse tras la cadena afectada (B,D) y señalar sin ambigüedad a un contenido a anexar, no admitiendo dos marcadores dentro de una subcadena ni dos referencias sucesivas (F)
A pesar de la sencillez de este procedimiento, ya podemos advertir que su utilidad supera ampliamente los recursos disponibles en OOo Basic en cuanto al manejo de cadenas. Gracias a esto es posible idear procedimientos de personalización de documentos basados en la técnica 'cloze': sobre un texto base, los contenidos a personalizar son asumidos por variables, las cuales, a su vez, son cumplimentadas por el usuario de modo interactivo mediante la función input (ver ejemplo). Y no acaban aquí las opciones.

Efectivamente, una tercera opción (o procedimiento, si se prefiere) de formateo de cadenas es el método cadena.format(valores):
  • La posición de los valores en la cadena-base se identifican con {}
  • Esta cadena se sitúa al inicio de la secuencia
  • El texto de reemplazo va precedido de la id de a función format()
  • ... y se puede hacer por posición de forma implícita o explicitando los valores posicionales...
  • ... o bien utilizando la forma diccionario clave:valor
Veamos ejemplos prácticos de todo ello

Primera forma.

print('Me llamo {}, vivo en {} y estudio {} en {}'.format('Elvira','Oviedo','Veterinaria', 'León.))

-> Me llamo Elvira, vivo en Oviedo y estudio Veterinaria en León.

 Segunda forma.

print('Dame {}, el {} y los {}'.format('la llave inglesa', 'destornillador', 'alicates'))
print('Dame{2}, la {0} y el {1}'.format('llave inglesa', 'destornillador', ' los alicates'))

NOTA 5 -> Las llaves {} indican la posición de los valores de .format(). Tanto en la primera forma como en la primera línea de código de la segunda, aunque no se especifica nada dentro de las llaves, es el orden mismo en que aparecen los valores .format() el que sirve para indicar la posición que ocuparán en el texto. LO que hago con la segunda línea de código de la segunda forma es alterar ese orden indicando dentro de las llaves un número-índice, que es la posición de las entradas en .format().

El texto resultante de 2.1 será  Dame la llave inglesa, el destornillador y los alicates y el de 2.2 Dame los alicates, la llave inglesa y el destornillador.

Tercera forma. Uso de variables.

 

nombre = 'Julia'
localidad = 'Gijón'
estudios = 'Albañilería'

print('Me llamo {}, vivo en {} y estudio {}'.format(nombre,localidad,estudios))

print('Me llamo {1}, vivo en {2} y estudio {0}'.format(nombre,localidad,estudios))

NOTA 6 -> En esta tercera forma sustituimos los valores directos por variables (lo que nos permite, por cierto. modificar interactivamente el contenido) y después empleamos las variables como contenido de .format(). las dos expresiones permiten comprobar que el uso de variables no modifica las propiedades y el modo de funcionar del procedimiento.

Cuarta forma.  Clave:Valor

print('Me llamo {nom}, vivo en {local} y estudio {estud}' .format (nom=nombre, local=localidad, estud=estudios))

NOTA 7 -> Sobre la misma base de contenido que en 3, ahora en 4  expongo la forma más evolucionada del procedimiento: una clave identifica y se diferencia del valor o contenido que no es otro que el nombre de las variables. Esa clave es la que se escribe dentro de las llaves, en sustitución y como alternativa del valor-índice empleado en formas previas. La ventaja de esta fórmula es que se asocia al uso de diccionarios.

Finalmente, la cuarta opción ya ha sido específicamente tratada en una entrada de este blog, por lo que no me detendré demasiado en ella. Se trata de las llamadas cadenas f (f-string o formatos literales), que se caracterizan porque su sintaxis incluye F o f al inicio de la cadena, en literales, antes de las comillas. Veamos un ejemplo:

nom1 = 'Juana'
nom2 = 'Lucas'
print(f'Una niña llamada {nom1} juega con un niño llamado {nom2}')

print(f'{nom1}') -> Juana

NOTA 7 ->  f-string y .format() son incompatibles, esto es: no se pueden usar en la misma concatenación. En el ejemplo anterior, tercera línea, tanto si eliminamos f como si añadimos format() para identificar la segunda variable o cualquier otra, se produce un funcionamiento anómalo.  Obsérvese en la cuarta línea cómo se utiliza f-string cuando iniciamos la cadena con una variable: necesitamos incluirla en una cadena literal, esto es: dentro de comillas (' '/" ") e identificada como tal variable entre llaves. Cualquier otra sintaxis provoca error o respuesta no deseada.

Contamos aun con una última forma de concatenar variables con cadenas, aunque esta quinta opción es en realidad una opción que podemos considerar complementaria de las anteriores, ya que no forma parte de los recursos comunes del lenguaje y precisa importar una librería específica llamada Template y las funciones asociadas a string.

 from string import Template

print(Template('Me gusta estudiar $leng').substitute(leng='Python'))

print(Template('Me gusta $accion $leng').substitute(accion='estudiar', leng='Python'))

NOTA 8 -> Puedes observar en estos dos ejemplos la sintaxis del procedimiento, en cierto modo recuerda al uso de funciones y más específicamente a .format():

  1. Primero llamamos a la función Template()
  2. Después incluimos dentro de cadena los marcadores, que se identifican mediante la expresión $NomVar.
  3. Y finalmente, mediante la función .susbtitude(), como parámetros de ellas en, y en formato clave:valor, damos contenido a las variables.