Está en la página 1de 27

Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

PENSAMIENTO COMPUTACIONAL (PC)


UNIDAD 1: conceptos básicos en programación y PC.
COMPUTADORA: dispositivo físico de procesamiento de datos con propósito general. Son
sistemas que integran dos aspectos:
SOFTWARE: conjunto de herramientas abstractas (programas). Es la componente
LÓGICA de un modelo computacional. → Dicta las tareas.
HARDWARE: componente físico del dispositivo. → Resuelve las tareas.
MODELO DE VON NEUMANN: tipo de arquitectura de una computadora. Consta de 3
componentes o subsistemas:
1) ENTRADA – SALIDA (E/S)
2) MEMORIA INTERNA (MI)
3) UNIDAD CENTRAL DE PROCESO (UCP)
↔ E/S ↔ MI ↔ UCP
→ La información ingresa a través de E/S. Los datos necesarios para el procesamiento
(incluyendo los programas) se alojan en la MI. El UCP transforma los datos.
← Los resultados que genera el UCP son guardados en la MI y mostrados al usuario a través
del módulo E/S.
MODELO DE FUNCIONAMIENTO DE PROGRAMA ALMACENADO: implica que un programa,
para poder ser ejecutado, debe estar cargado previamente en la memoria interna (MI).
SISTEMA OPERATIVO (SO): programa encargado de administrar el equipo y responder a los
programas que piden servicio.
PROGRAMA: un Programa de Computadora es un Algoritmo escrito en un Lenguaje de
Programación (suma de algoritmo y datos).
DATO: representación simbólica ya sea numérica o alfabética, cuyo valor está listo para
ser procesado por un ordenador y mostrarlo a un usuario en modo de información
(diferenciamos dato de información considerando que información es asignarle un
significado al dato).
ALGORITMO: serie finita de pasos precisos para alcanzar un objetivo.
SERIE: conjunto ordenado.
FINITA: tiene un fin y este es bien específico.
PRECISOS: pautas claras y no ambiguas.
Pasos a seguir para escribir un algoritmo:
1) Análisis del problema.
2) Primer esbozo de solución.
3) División del problema en partes.
4) Ensamble de las partes.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

EVENTOS QUE PUEDE DIFERENCIAR UNA COMPUTADORA: Presencia de Tensión o Ausencia


de Tensión. Estos estados son disjuntos y complementarios. Toda la realidad digital se modela
empleando un sistema matemático binario (de base dos). Se emplean unidades de medida
como:
BIT: unidad mínima de medida. Corresponde a un dígito binario (0 o 1).
BYTE: 8 bits.
KBYTE: 1024 bytes.
MEGABYTE: 1024 kbytes.
LENGUAJE DE PROGRAMACIÓN: protocolo de comunicación.
LENGUAJES CON FUERTE “POLISEMIA”: sus elementos son infinitos y pueden tener
múltiples significados. Por ejemplo, los lenguajes de imágenes estáticas (como los
cuadros) o en movimiento (como las películas), composiciones musicales o emojis.
LENGUAJES FORMALES: tienen un conjunto acotado y definido de elementos válidos
(tokens o palabras) y reglas de construcción específicas de sentencias u oraciones
válidas (reglas de sintaxis). Por ejemplo, el lenguaje matemático, las partituras
musicales, etc.

LENGUAJE ASSEMBLER: lenguaje de programación “ensamblador” de bajo nivel que se basa


en un conjunto de órdenes (primitivas) conocidas por el procesador.

PROGRAMAS FUENTE: escritos en lenguaje de alto nivel. Es un archivo de instrucciones.


TRADUCTORES: pasar de un programa de lenguaje de alto nivel a su versión en “assembler”
(bajo nivel). Existen dos tipos de programas traductores:
INTÉRPRETES: traduce sentencia a sentencia (o línea a línea), a medida que se solicita
su ejecución (por ejemplo, Python). → Puede haber errores de ejecución.
COMPILADORES: verifica línea a línea y ejecuta completamente el programa. → Puede
haber errores en la verificación (compilación) y/o en la ejecución.

AMBIENTES CENTRADOS EN LENGUAJES O ENTORNOS DE DESARROLLO: aplicaciones que


permiten trabajar en un programa durante todo su ciclo de vida.
LENGUAJE PYTHON: lenguaje de alto nivel, interpretable, multi paradigma, multi plataforma,
abierto. Permite el desarrollo rápido de aplicaciones (RAD). Posee una excelente
administración de memoria. En la actualidad es uno de los lenguajes más estudiados y su uso
para la construcción de aplicaciones variadas va en aumento.
MULTI PARADIGMA: puede construir programas con distintos enfoques o modelos de
resolución de problemas usando el mismo lenguaje. Y mezclar paradigmas en un
mismo programa.
MULTI PLATAFORMA: un programa en este lenguaje puede ejecutarse en distintos SO.
ABIERTO: se puede ver el código de las herramientas que provee. En consecuencia, es
gratuito.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

LIBRERÍAS, BIBLIOTECAS O MÓDULOS: manera más simple de extender un paquete de


herramientas.

VARIABLES: “cajas donde guardas cosas (valores)”. Están guardadas en la memoria.


VALORES: información importante para el programa. Pueden tener forma de números,
letras, palabras o símbolos.
ASIGNACIÓN: operación que se utiliza para guardar los valores dentro de las variables.
Se representa con un signo =.
Variable 1 = Valor 1. “Dónde queremos guardar = qué queremos guardar”.
También podemos guardar variables dentro de variables. Variable 2 = Variable 1; es
decir, guardo la variable 1 (con sus valores) dentro de la variable 2.
Las variables se modifican con operadores.
FUNCIÓN print():
Primer programa de “imprimir”. Para mostrar un mensaje en pantalla uso “”.
print(“mensaje”)
Uso de variables y valores.
año_actual = 2023
Combinación de ambos.
print(“Estamos en el año:”)
print(año_actual)

También puedo “enganchar los valores” en una sola línea usando la coma (,).
print(“Estamos en el año:”, año_actual)
Operación suma:
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

UNIDAD 2: sentencias básicas y datos simples en PYTHON 3.x.


FLUJO DE CONTROL DE UN PROGRAMA (FCP): secuencia en que las sentencias se ejecutan.
FLUJO DE CONTROL ESTÁNDAR EN PYTHON: ejecución de la primera a la última sentencia,
una por una.
FUNCIONES: se usan escribiendo nombre (de la función) y a continuación, entre paréntesis,
los parámetros y argumentos (datos que se envían para que se realice la tarea).
Para leer, ingresar o permitir que el usurario ingrese datos, se utiliza la función input().
Para mostrar toda la información que el programador considere debe mostrársele al usuario
se emplea la función print().
DATOS:

CONSTANTES: datos inalterables (no cambia su valor). Para referenciarla en un


programa, se la llama por su valor. Fuertemente “tipados”.

VARIABLES: dato que puede cambiar o variar su valor. Se utiliza un nombre (o etiqueta)
para poder identificarlo y emplearlo; puede ser cualquier combinación de letras y
números. Débilmente “tipados”.
Nota: no se puede emplear el mismo nombre para dos datos diferentes; una variable puede
referenciar un sólo dato por vez. Por lo tanto, si uso un mismo nombre para un nuevo dato
se pierde el valor anterior (se pisa el dato anterior).
TIPOS PREDEFINIDOS DE DATOS:

Nota: - str (string) significa cadena de caracteres.


- Si trabajo con números float, me va a devolver tipo float. Y la operación división
siempre devuelve float.
SECUENCIA: es un conjunto contiguo de elementos con una organización interna. Cada
caracter ocupa una posición determinada. En este lenguaje no diferenciamos mucho un
caracter de un texto; ya que manipulamos un caracter como un texto de un único elemento.
Por ejemplo, el texto: 'hola' no es igual a 'aloh' y tampoco a 'Holá'.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

OPERACIONES VÁLIDAS:
EDICIÓN DE TEXTO: transformar textos o cadenas de caracteres (string).

FUNCIÓN len(): cuenta las letras del string o su “largo”.


Otras operaciones para strings:
palabra 1 + “ “ + palabra 2: concatenación de 2 string con espacio en medio.
[::] : cortar un string; nombre[desde_donde:hasta_donde:cada_cuantas_letras] o
nombre[start:stop:step]. Ejemplo: nombre[5:8:2], empezará a contar desde la posición 5,
parará en la posición 8 e imprimirá cada 2 posiciones. Puedo usarlos por separado también,
es decir, [5::], [:8:], [::2].
Nota: la primera posición es 0.
OPERACIONES MATEMÁTICAS O ARITMÉTICAS: transformar datos numéricos.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

CADENA DE FORMATEO: cuando queramos introducir datos que van a cambiar (variables)
dentro de un texto constante, colocamos una f (puede ser f o F), a continuación, el texto
encerrado entre comillas (texto constante) y en el lugar en que queramos que aparezca el
dato variable colocamos el nombre de la variable encerrado entre {}.

FUNCIÓN DE CASTEO int(): convierte y devuelve el dato que se le envía entre paréntesis como
número entero. El dato enviado debe ser compatible con un valor numérico entero. Si lo que
recibe int() es un número con precisión decimal devuelve sólo su parte entera.
Programa sin int(). Toma los números como string y los concatena.

Programa con int(). Toma los números como enteros y los suma.

Nota: en Python podemos dejar comentarios que solo serán leídos por el programador; para
comentar una línea empleamos el símbolo # (todo lo que esté escrito a la derecha del símbolo
en la misma línea será ignorado en la ejecución). Si tenemos que explicar mucho y vamos a
utilizar varias líneas, sólo empleamos triple comilla “”” (simples o dobles) al comenzar el
comentario y lo cerramos con otro juego de triple comilla.
CREAR FUNCIONES PROPIAS: cuando trabajamos con funciones que no son predefinidas
antes de usarlas (invocarlas) debemos hacer que Python las conozca. Este proceso se llama
definición y se compone de:
- Encabezado o firma de la función: def seguido del nombre que le pondremos a la función.
Luego (); dentro del paréntesis indicaremos los nombres de todos los parámetros separados
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

por coma. Los parámetros serán las variables en las que se van a copiar los datos que se
enviarán a la función (cuando sea invocada) para que pueda ejecutarse. Terminaremos con:
- Cuerpo: detallaremos qué hará la función. Para que Python sepa dónde acaba ese cuerpo,
debemos desplazarlo hacia la derecha un poco y cada línea del cuerpo deberá comenzar a la
misma altura (indentación). Para utilizar los parámetros uso la operación de concatenar
“+parámetro+”.
- Finalización: la función se termina cuando “corto el ámbito”, es decir, cuando concluyo con
la alineación o indentación del cuerpo.
Para invocar la función utilizo nombre(). Si utilizo parámetros será nombre(parámetro).

Nota: todas las sentencias que tienen una o varias líneas de encabezado (se llaman Sentencias
Estructuradas) tienen su cuerpo indentado.
Para que una función devuelva un resultado será necesario elegir uno de los datos que
maneja (sólo uno) y devolverlo a quién invocó a la función empleando la sentencia return.
Escribiremos return y el dato.

Para devolver más de un resultado o parámetro:

FUNCIÓN eval(): permite obtener el resultado de evaluar el texto que se le pasa como
argumento, siempre que el mismo sea compatible con una expresión aritmética válida.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

FUNCIÓN type(): indica el tipo de variable que estamos utilizando. Si lo combino con
print(type()), imprime el tipo de la variable en pantalla.

Introducción FUNCIÓN if y else: se utiliza cuando tengo una “pregunta”. If: “si pasa tal cosa,
hago esto”; Else: “sino, hago esto”.

Nota: el doble igual “==” se utiliza para conocer si un valor es igual a otro valor, es decir
devuelve un valor “true” (verdadero) o “false” (falso), dependiendo de la comparación.
FUNCIÓN repr(): convierte un entero int() en una cadena str() para poder concatenarlo con
otra cadena str().
FUNCIÓN format(): imprime con un cierto formato. Por ejemplo, para imprimir “Apellido,
Nombre” puedo usar: print(“{}, {}”.format(apellido, nombre), dentro de las {} iran las
variables en su correspondiente orden.
Nota: también puedo utilizar ‘f’ para acomodar variables. Por ejemplo: print(f”{apellido},
{nombre}”).
FUNCIÓN replace(): reemplaza caracteres. Por ejemplo, si quiero quitar letras de una palabra
puedo usar: palabra.replace(‘a’, ‘ ‘).

UNIDAD 3: estructuras de control.


INTRODUCCIÓN A CONDICIONALES
Si se presentan casos o patrones que requieren tratamientos diferenciados, deberemos
escribir programas que exhiban dos capacidades importantes. En primer lugar, deben poder
identificar los casos o patrones; es decir, las alternativas, en general, que se puedan presentar
(pueden ser patrones ingresados o producidos internamente). En segundo término, tendrán
que saber elegir diferentes caminos de acción para cada caso. Para esto utilizamos el Modelo
Lógico.
INTRODUCCIÓN A LÓGICA SIMBÓLICA, BIVALENTE O BINOMIAL (ÁLGEBRA DE BOOLE)
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

En Lógica tenemos expresiones lógicas (proposición, expresión booleana o condición) a las


que se asocia algún valor (valor de verdad) posible del Universo de valores disponibles. La
Lógica Binomial tiene un Espacio de Resultados finito: U={V,F} (Verdadero V o Falso F). Estos
valores son disjuntos y complementarios; es decir, no existen las verdades a medias, si algo
no es Verdadero, entonces es Falso; y viceversa. Datos Booleanos: True==1 (n!=0), False==0.
Nota: se utilizan las últimas letras del alfabeto en minúsculas para denotar una expresión
lógica. Por ejemplo: p, q, r, s.
OPERACIONES LÓGICAS: se utilizan para escribir las condiciones de los programas.
NEGACIÓN: ~ (equivalente a NO/not).
CONJUNCIÓN: ^ (agregado, suma, equivalente a y/and).
DISYUNCIÓN: v (opcionalidad, alternativaas, equivalente a o/or).
TABLAS DE VERDAD PARA OPERACIONES LÓGICAS
Si un operador es monario (se aplica a un solo término, como la negación ~), sólo habrá (2x1)
resultados posibles.
Si un operador es binario (se aplica a dos términos, como la conjunción ^ o la disyunción v),
sólo habrá (2x2) resultados posibles.

Nota: Negación: siempre da el valor complementario.


Conjunción: es V, sólo si las dos expresiones son V.
Disyunción: es F, sólo si las dos expresiones son F.
OPERADORES DIRECTOS
OPERADORES RELACIONALES (DE COMPARACIÓN O DESIGUALDAD): comparar valores
de datos.
- ==igual.
- != distinto.
- < menor.
- <= menor o igual.
- > mayor.
- >= mayor o igual.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

OPERADORES DE PERTENENCIA: averiguar si un valor está dentro de un grupo.


- in: pertenece.
- not in: no pertenece.
EXPRESIONES: conjunto de operaciones relacionales “enganchadas” por operaciones lógicas.
Tienen un valor de verdad.
SENTENCIAS CONDICIONALES
Son un tipo de Estructuras de Control, por lo que permiten alterar el Flujo de Control Normal
(FCP) de un programa. Tienen dos componentes:
- Condición: permite al programa formularse y responderse una pregunta.
- “Accionar”: le permite al programa tomar sólo uno de dos (o más) posibles cursos
de acción.
FUNCIÓN if: Python dispone de esta Sentencia Condicional bastante genérica y versátil, que
usaremos para tomar decisiones y hacer bifurcaciones en general.
Cuando el Flujo de Control de un Programa (FCP) alcanza una sentencia if, lo primero que se
hace es evaluar la condición asociada a ella. Si la condición da V (o sea, si la respuesta a la
pregunta es SI), se ejecutarán las sentencias asociadas a esa elección y no a la salida por F (la
respuesta es NO). Es decir, el FCP llega a la condición, la evalúa y luego bifurca. Hay un grupo
de sentencias que, en ese caso (debido a la respuesta obtenida), no se ejecutarán. En algún
punto la bifurcación acaba y el FCP sigue su curso de manera unificada (al menos hasta la
próxima bifurcación).
Puede ser que a un if, le siga otra sentencia if. Pero, en realidad el FCP al arribar a la segunda
sentencia, no sabe por dónde atravesó la primera. El FCP tiene “amnesia regular”, sólo sabe
cuál es la sentencia que está ejecutando en ese momento y cuál será la próxima.
Es muy frecuente también escribir if que no hace nada por la salida del F, sólo por V. Esto es
para cuando hay que sumar alguna acción a lo general para ciertos casos.
Además, dentro de if, Python puede encontrar un Encabezado elif o un Encabezado else;
sabe entonces que continúa dentro del if, pero esa es otra rama. Si viene ejecutando el
Cuerpo de la rama del si, de todas maneras va a ignorar esas ramas. Y sigue ignorando hasta
encontrar la primera sentencia alineada al if (o más a la izquierda) que no sea ni elif, ni else.
Dentro de una rama de if aparece otro if decimos que los if están anidados.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

CICLO, BUCLE O SENTENCIA ITERATIVA: máquina repetidora de pasos o instrucciones en un


programa. Son primitivas sencillas, con una sintaxis simple. Puede existir una cantidad
constante de repeticiones (conociendo la cantidad) o una cantidad variable de repeticiones
(posponiéndolas). Todos los lenguajes de tipo procedural (utilizan variables y datos
almacenados) disponen de una o más sentencias de este tipo. Sus componentes son:
- Invariante: permite decidir si se ejecutará el cuerpo de ese bucle una vez o más o se
abandonará el mismo para proseguir con la sentencia al seiguiente ciclo, es decir,
determina cuándo ejecutar el cuerpo y cuando finalizar definitivamente. Se escribe como
una condición válida.
- Cuerpo: conjunto de sentencias válidas que se ejecutan repetidamente hasta que la
evaluación de la condición o Invariante diga lo contrario.
FUNCIONAMIENTO DEL CICLO: cuando el FCP llega a un ciclo, lo primero que hace es evaluar
la condición (llave de entrada/salida). Si la condición es verdadero, se ejecuta completamente
el cuerpo; cuando se termina de ejecutar el mismo, el FCP vuelve a la condición (bucle) y esta
se evalúa nuevamente. Si vuelve a dar verdadero se repite todo el proceso anterior; si en
cambio da falso, el FCP abandona el ciclo y ejecuta la sentencia siguiente al bucle.
CICLOS ANIDADOS: ciclo dentro de otro ciclo. Cuando el FCP ingrese a un ciclo interno (que
está dentro de otro más externo), se quedará repitiendo este hasta finalizar y recién
proseguirá ejecutando el cuerpo del más externo.
SENTENCIA while: cuando el FCP llega a una sentencia while lo primero que se realiza es la
evaluación de la condición. Si la condición da verdadero se ejecutará completamente el
Bloque de Sentencias del Cuerpo del while. Cuando termina la ejecución del Cuerpo el FCP
vuelve a la condición y la evalúa nuevamente. Si da verdadero repite lo anterior; si da falso
sale del while y ejecuta la sentencia siguiente al mismo. Si la primera vez que el FCP llega al
while y evalúa la Condición da falso, el FCP sigue de largo a la próxima sentencia y saltea todo
el Cuerpo.
ASPECTOS IMPORTANTES DE UNA ITERACIÓN
- Cuerpo: porción del código que se repetirá de forma contigua. Se posiciona desplazado a
la derecha, ya que debe ir indentado.
- Condición: sirve de “puerta vaivén”. Debe poder abrirse para ingresar a ejecutar el Cuerpo
todas las veces que haga falta y cerrarse definitivamente cuando ya repetimos el mismo
las veces necesarias.
- Estado Previo: valor que controla cada una de las variables que forman parte de la
Condición antes de comenzar a ejecutar el Bucle.
- Paso o Avance: evita entrar en un loop infinito de repeticiones.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

SENTENCIA for: es un Ciclo que tiene automatizado, o incorporado al encabezado, el manejo


del Estado Previo y del Paso; es decir, actualiza por sí mismo la variable de iteración. Sin
embargo, no controlamos el “cómo” avanza, a diferencia de la sentencia while.
for var_ctrl in iterable:

FUNCIÓN range(): un rango es un constructor o estructura de control que permite generar


una sucesión de números enteros, con un cierto paso o salto regular.

range([<valor_inicio>,]<valor_fin>[,<paso>]) o range (start, stop, step)

Nota:
- El paso debe ser un entero.
- valor_inicio está incluído en el rango; valor_fin NO.
- valor_inicio es opcional; si no se coloca se asume 0.
- paso es opcional; si no se coloca se asume 1.
- Si se desea rango decreciente se debe colocar un paso negativo.
- Si se coloca paso debe colocarse valor_inicio, aunque este último sea 0.
- Para construir un rango puedo usar expresiones más complejas (como operaciones), no
deben ser sólo constantes o variables. Lo único relevante es que el valor resultante sea
entero.
FUNCIÓN list(): crea una lista. Ejemplo: list([1, 2, 3]) o list([“a”, “b”, “c”]).
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

FUNCIÓN abs(): calcula el valor absoluto de un número, es decir, devuelve su módulo.


FUNCIÓN size(): devuelve el número de elementos que hay en el array.
FUNCIÓN pass: saltea una ejecución.
FUNCIÓN break: permite parar un bucle por completo en cuanto se da o deja de darse una
condición externa; suele estar situado después de una sentencia if.
UNIDAD 4: tipos de datos estructurados.
Nota: los métodos se diferencian de las funciones porque siempre se invocan vinculados a
un dato o tipo de dato.
SECUENCIAS (TYPE SEQUENCE): es un grupo de elementos con una organización interna; que
se alojan de manera contigua en la memoria.
MÉTODO DE LA SECUENCIA
s.count(elemento|substring[,desde[,hasta]]): devuelve la cantidad de veces que aparece el
elemento|substring en la secuencia s.
s.index(elemento|substring[,desde[,hasta]]): devuelve la posición de la primera ocurrencia
elemento o del comienzo de substring en s.
CADENAS (TYPE STRING): subclase de secuencia inmutable que sólo admite caracteres como
elementos. Estos elementos se guardan en una variable txt de la siguiente forma:

MÉTODOS DE LA CLASE STRING


s.capitalize(): devuelve una copia del string con la primera letra en mayúscula y el resto en
minúscula.
s.center(ancho[,relleno]): string centrado con ese relleno a los costados.
s.find(substring[,desde[,hasta]]): devuelve la primera posición de comienzo del substring en
s.
s.rfind(substring[,desde[,hasta]]): devuelve la última posición de comienzo del substring en
s.
s.rindex(substring[,desde[,hasta]]): idem a s.rfind.
s.format(args,*): devuelve s formateada sustituyendo dinámicamente un texto.
Ejemplo: texto="Bruto: ${bruto} + IVA: ${iva} = Neto: ${neto}"
print(texto.format(bruto=100,iva=21,neto=121))
Retorna:
Bruto: $100 + IVA: $21 = Neto: $121
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

s.join(iterable): arma una string uniendo los elementos de iterable e intercalándolos con s.
Ejemplo: tup=(‘a’,’b’,’c’)
print(‘-‘.join(tup))
Retorna:
a-b-c
s.ljust(ancho[,relleno]): justifica hacia la izquierda.
s.rjust(ancho[,relleno]): justifica a derecha.
s.lower(): devuelve s en minúsculas.
s.upper(): devuelve s en mayúsculas.
s.maketrans(x[,y[,z]]): asocia en un diccionario los correspondientes caracteres de las
cadenas x e y.
s.translate(pares): devuelve s con los caracteres asociados en el diccionario pares
remplazados.
Ejemplo: vocales="aeiou"
acentos="áéíóú"
texto="murcielagos"
parejas=texto.maketrans(vocales,acentos)
print(texto.translate(parejas)
Retorna:
Múrcíélágós
s.replace(antes,ahora[,cantidad]): reemplaza el substring de antes por el de ahora.
s.strip(): elimina los espacios del inicio y fin del string.
s.lstrip(): elimina los espacios del inicio.
s.rstrip(): elimina los espacios del fin.
s.swapcase(): devuelve s con las mayúsculas convertidas en minúsculas y viceversa.
s.split([separador[,maximaDivision]]): devuelve una lista cuyos elementos son las partes del
texto separadas por separador. Si se omite separador toma blancos.
s.rsplit([separador[,maximaDivision]]): ídem a derecha.
s.startswith(prefijo[,desde[,hasta]): devuelve True si s comienza con prefijo, si no False.
s.endswith(sufijo[,desde[,hasta]): devuelve True si s termina con sufijo, si no False.
s.zfill(ancho): rellena con ceros a la izquierda hasta el ancho.
s.title(): devuelve s en minúsculas con cada palabra inicializada en mayúsculas.
s.isnumeric(): devuelve True si s es numérico, si no False.
s.isalnum(): devuelve True si s es alfanumérico, si no False.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

s.isalpha(): devuelve True si s es alfabético, si no False.


s.isdecimal(): devuelve True si s tiene sólo dígitos decimales (0-9), si no False.
s.isdigit(): devuelve True si s tiene sólo números (los dígitos decimales de 0 a 9), si no False.
s.isprintable(): devuelve True si s es un carácter imprimible, si no False.
s.isspace(): devuelve True si s es espacio, si no False.
s.istitle(): devuelve True si s es un título, si no False.
s.isidentifier(): devuelve True si s es un nombre válido de objeto, si no False.
s.isupper(): devuelve True si s está en mayúsculas, si no False.
s.islower(): devuelve True si s está en minúsculas, si no False.
TUPLAS (TYPE TUPLE): subclase de secuencia inmutable y heterogénea de elemetos que
pueden ser de cualquier tipo (no solo caracteres; incluso otras estructuras); es decir, una
tupla es una Estructura de Datos Genérica. Se escriben entre () y sus elementos van
separados por coma, por ejemplo: (8,) - () - (1,2, 'hola') - ((a, 2),b). También se pueden escribir
sin los paréntesis (salvo que estén dentro de otra estructura de datos): (a, 2),b - 2,3,4,10.
Nota: inmutable quiere decir que una vez que los datos de las variables se alojan en la
memoria, no pueden cambiarse. Si necesito cambiar su valor o modificar algo, se guardará la
versión útil en otra parte de la memoria y la etiqueta (el nombre de la variable) apuntará al
inicio de la nueva región de memoria. La versión anterior quedará abandonada y descolgada
a la espera de que pase el proceso de recolección de basura (el packman de memoria que
recupera como libres los espacios de memoria ocupados por datos descolgados e
inaccesibles). Por lo tanto, si quiero modificar el contenido original debo pisarlo (igual que
con una variable simple o una string).
LISTAS (TYPE LIST): subclase de secuencia genérica mutable (pueden cambiar de tamaño y
contenido total, o por partes) y heterogéneas (pueden contener cualquier tipo de objeto,
incluyendo otras listas; brinda la utilidad de una estructura record). Tienen la facilidad de
acceso de los arreglos (array), lo que permite utilizar algoritmos más sencillos de
manipulación de datos, sin perder la flexibilidad dinámica. Se escriben entre [].
Nota: record significa regristro, es un tipo de dato estructurado formado por la unión de
varios elementos bajo una misma estructura.
PRIMER ATAJO: cada vez que se asigna a una lista (no a un elemento) un objeto
constante (como [1, 2, 3],[],[‘hola’]), la lista apunta a una región de memoria propia.
Pero como es un objeto mutable también admite que se modifique un elemento en
particular, o un subgrupo. Al ser una secuencia, admite todas las reglas de
referenciación de un elemento o parte de elementos que conocemos para secuencias.
SEGUNDO ATAJO: si quiero obtener una copia efectiva de una lista (en otra región de
memoria) no alcanza con asignar esa lista a otra variable de este modo: a= [1, 2, 3] 
b= a; esto solo produciría dos maneras diferentes de acceder a los mismos datos.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

Si efectivamente queremos tener dos estructuras separadas para que al modificar los
datos de una se preserven los datos originales en otra debemos forzar su copia; para
esto utilizamos el método copy(), que copiará los contenidos de la lista “a” en otra
parte de la memoria.
MÉTODO copy(): b=a.copy().
MÉTODOS DE LA CLASE LIST
MÉTODOS PARA AGREGAR ELEMENTOS A LISTAS: append(), insert(), extend().
MÉTODOS PARA QUITAR ELEMENTOS DE LAS LISTAS: pop(), remove(),clear().
miLista.append(valor): agrega el elemento valor al final de miLista.
miLista.insert(posic,valor): inserta el elemento valor en la posición posic.
miLista.extend(otraLista): agrega al final de miLista otraLista.
miLista.pop([índice]): quita de miLista el elemento de la posición índice. Si no se usa este
parámetro, quita el último elemento. Este método devuelve el valor retirado.
miLista.remove(valor): quita de miLista el elemento valor.
miLista.sort([reverse=True][,key=función]): ordena miLista; si se emplea el parámetro
reverse: en orden descendente; si se usa key, con criterio de ordenamiento función.
miLista.reverse(): invierte el orden de miLista (el primero pasa a ser el último).
miLista.count(valor): cuenta la cantidad de apariciones de valor en miLista.
miLista.index(valor): devuelve la posición de la primera aparición de valor en miLista.
Nota: todas las funciones y métodos que no devuelven nada (no hay return en su cuerpo)
devuelven la constante nula NONE.
ACCEDER A ELEMENTOS DE TUPLAS Y LISTAS
Para poder acceder a objetos individuales que están dentro de otros se debe referenciar cada
nivel por separado, de izquierda a derecha, siguiendo el orden desde el más externo al más
interno. Contando la posición inicial como [0].

“DESEMPAQUETAMIENTO”
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

BUSCAR ELEMENTOS EN LISTAS


- 1ra búsqueda: devuelve True (si el elemento está en la lista) o False (si el elemento NO
está en la lista).

- 2da búsqueda: utilizando la función .index(); devuelve la posición en la que se encuentra


el elemento solicitado. Si el elemento no se encunetra en la lista, devuelve un error.

ORDENAMIENTO DE ESTRUCTURAS
FUNCIÓN sorted(): se puede aplicar a listas, tuplas o strings. No toca la estructura y devuelve
una versión ordenada (una lista).
MÉTODO sort(): unicamente aplicable en listas. Modifica la lista sobre la que se aplica el
ordenamiento y no devuelve nada (NONE).

Todos los elementos de la secuencia a ordenar deben ser comparables; es decir, deben tener
tipos compatibles para comparación (números con números, string con string, etc.). Si no se
pueden comparar entre si, no hay forma de decidir cuál es menor o mayor.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

MÉTODO reverse(): aplicable a listas; tanto sort() como sorted() disponen de este argumento
opcional, que por defecto está seteado en False. Si deseamos ordenar una lista o secuencia
en forma descendente bastará con prender en True este parámetro.

MÉTODO reversed(): aplicable a secuencias; devuelve la secuencia en orden invertido (no


ordenada).

ORDENAMIENTO DE ESTRUCTURAS NO ESTÁNDAR


Utilizando la sentencia for y el parámetro key (con la función len() en este caso):
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

DEFINIR CRITERIOS DE ORDENACIÓN PROPIOS

TRASFORMACIÓN Y FILTRADO DE SECUENCIAS


FUNCIÓN map(): aplica una función cualquiera a cada elemento de una secuencia y genera
otra secuencia con los resultados. Sintaxis: map(función,secuencia).

FUNCIÓN filter(): selecciona elementos de una secuencia de acuerdo a la evaluación hecha


por una función booleana (función que devuelve sólo True o False) y entrega la secuencia con
los elementos que pasaron el filtro (la función devolvió True). Sintaxis:
filter(función,secuencia).
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

Nota: estas son funciones comprimidas; todo el bucle queda incluido en la función. Para
simplificar sus salidas, aplicamos un casteo y convertimos la salida a lista.
DICCIONARIOS (TYPE DICT): estructura mutable que permite mapear un valor (clave) con un
grupo de datos. De este modo, si precisamos acceder a un valor en particular, no necesitamos
indicar su posición (de hecho, no la sabremos), sólo indicando su valor saltamos directamente
a él. Es decir, empleamos el dato como clave de acceso. Ubica sus elementos en memoria de
acuerdo al resultado que obtiene al aplicar una función de cálculo a su clave.
Un diccionario también se denomina como Estructura Hashable o con acceso hash; haciendo
referencia al nombre que se le da a los algoritmos de cálculo de direcciones para acceso
directo a la información (Algoritmos Hash).
Un diccionario Python se escribe como una colección de pares, separados por ‘:’, entre llaves
{}. Para referenciar a cualquier valor vinculado a una clave sólo se requiere indicar la clave en
lugar de la posición, utilizando corchetes [].

Para sumar un elemento a un diccionario no necesitamos hacer lugar como en las listas.
Simplemente asignamos el par clave:valor. Pero si la clave ya existía, el valor anterior es
pisado. Sintaxis: diccionario[clave]=valor.

Nota: las claves de un diccionario pueden ser números, booleanos, strings, tuplas y cualquier
tipo de Python que NO admita cambios (inmutable). No pueden cambiar, porque si no,
cambiaría el resultado de la función hash aplicada a ellas y se debería reubicar la información.
Por contraposición, los datos o valores vinculados a una clave pueden ser de cualquier tipo
simple o estructurado (listas y diccionarios quedan incluidos acá).
MÉTODOS DE LA CLASE DICT
d.clear(): elimina todos los elementos de d.
d.pop(clave): remueve el par clave:valor del diccionario y devuelve valor. Si la clave no está
da error.
d.popitem(): remueve y devuelve cualquier par clave:valor. Si d está vacío da error.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

d.copy(): devuelve una copia de d en otra región de memoria.


d.fromkeys(secuencia): crea el diccionario d, tomando las claves de una secuencia.
d.get(clave): devuelve el valor asociado a clave.
d.keys(): devuelve una lista con las claves de d.
d.values(): devuelve una lista con los valores de d.
d.items(): devuelve una lista de tuplas con las claves y valores de d.
d.update(b): agrega los pares clave:valor de b a d. Si alguna clave existe, actualiza su valor.

ORDENAMIENTO DE DICCIONARIOS
Para ordenar un diccionario podemos armar una lista de claves, ordenarla (utilizando la
función sorted()), y luego emplear esa lista para acceder a los datos del diccionario.
Llamamos a la lista ordenada: Índice.
UNIDAD 5: archivos y manejo de errores.
ARCHIVOS: Estructuras de Datos persistentes; perduran más allá de la vida de quien las
genera. Estos se guardan en dispositivos que administran datos no volátiles; no desaparecen
cuando se apaga el equipo. Los archivos se emplean para compartir información entre
usuarios, equipos, etc.
Los datos se graban como archivos, con un nombre, una extensión (por ejemplo, txt o csv) y
una organización interna conveniente, con el fin de que “sobrevivan” a la ejecución corriente
y poder utilizarlos a futuro.
BUFFERS: “miniespacios” de intercambio entre un programa y un archivo. Los archivos
contienen grandes volúmenes de datos, por ello, se copian sus datos por partes en la MI; a
medida que modificamos información, resguardamos una copia en el archivo. Como ya se
sabe que vamos a estar trayendo y enviando datos al archivo de manera continua, en lugar
de generar un montón de variables, el SO reserva una zona de memoria (que está dentro de
la región del programa que emplea el archivo) y la establece como el canal de comunicación.
Para esto, los SO implementan un sistema de tráfico de intercambio, lectura y escritura en
bloque. Es decir, si el programa necesita un dato en particular, no se trae sólo ese dato, sino
todo un bloque de información.
ABRIR ARCHIVO: proceso de petición al SO para que defina o asigne un buffer (preparar el
canal de comunicación); se utilizan datos relevantes, como nombre, ubicación en la
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

estructura de directorios, qué haremos con el archivo, etc. Además, se le asigna un nombre
interno (alias) para referenciarlo dentro del programa, ya que un mismo programa puede
abrir simultáneamente todos los archivos que necesite.
PRIMITIVAS GENERALES
FUNCIÓN open(): abre un archivo en Python; le solicita al SO que establezca el vínculo de
trabajo y devuelva la dirección de inicio del buffer del archivo. Tal dirección debe ser
guardada en una variable, ya que será el nombre interno del archivo desde ese momento.
Una vez abierto, referenciamos al archivo por su nombre interno o alias. Esta función abre el
archivo con la codificación predeterminada del SO. Sintaxis:
open(“nombre_completo_archivo”,modo)
PARÁMETRO encoding: acompaña a la función open(); nos asegura que el archivo sea abierto
correctamente con la codificación deseada. Sintaxis:
open(“nombre_completo_archivo”,modo,encoding=codificación)
MÉTODO close(): cierra el archivo, obligándolo a grabar todo lo que haya quedad pendiente
y liberando espacio del buffer. Sintaxis:
nombre_interno_archivo.close()
MODOS
‘r’ = lectura.
‘r+’ = lectura/escritura.
‘w’ = sobreescritura/reescritura. Si no existe un archivo, se creará.
‘a’ = añadir. Escribe al final del archivo.
Nota: por defecto, la conexión se abre en modo lectura.
ARCHIVOS PLANOS: archivos de texto simple, texto sencillo o texto sin formato; archivos
informáticos que contienen únicamente texto formado solo por caracteres que son legibles
por humanos y carece de cualquier tipo de formato tipográfico. Por ejemplo, archivos txt o
csv.
MÉTODOS DE ARCHIVOS DE TEXTO (TYPE TXT)
MÉTODOS DE LECTURA: read(), readline(), readlines().
MÉTODOS DE ESCRITURA: write(), writelines().
arch.read(): permite leer una determinada cantidad de bytes, si no se pone nada, lee hasta
el final.
arch.readline(): lee la siguiente línea (caracteres hasta el próximo \n).
arch.readlines(): permite leer varias líneas, si no se indica nada, lee todas las que falten.
Devuelve una lista de cadenas del contenido del archivo.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

arch.write(): permite escribir en el archivo lo que se envía como argumento.


arch.writelines([línea1,línea2]): permite escribir varias líneas en un archivo.
Nota: \n es un carácter de control, por eso se escribe con dos caracteres, el primero \ que
indica que lo que sigue debe ser interpretado de manera especial. \n es el carácter de bajada
de línea. Cuando agregamos \n al final de cualquier texto, si lo mostramos por pantalla al
finalizar el texto el cursor baja una línea.

ARCHIVOS DE TEXTO – COMMA SEPARATED VALUES (TYPE CSV): el formato csv se refiere a
“valores separados por comas”. El carácter de separación de campos puede variar entre
coma o punto y coma (‘,’ o ‘;’) dependiendo del programa que lo abre.
Estos archivos están asociados a la creación de tablas de contenido (en filas y columnas).
Usualmente se utiliza Excel para su apertura.
Nota: para averiguar qué carácter de separación se debe emplear en un programa, puedo
generar un archivo csv en una Planilla y luego abrirlo en un editor simple de texto, donde
figurarán los símbolos que separan los campos. Python utiliza ;.

Puedo “procesar” o “transformar” los datos del archivo para que se impriman con otra
estructura.

MANEJO DE ERRORES
El ingreso de datos es uno de los puntos de mayor frecuencia de errores y es el lugar más
económico donde se pueden detectar y salvar los mismos. Para intentar disminuir la tasa de
errores en el ingreso se puede simplificar el formato de los datos o adaptarlos a sistema de
lectura automática (códigos de barra, qr, etc.), y/o validar los ingresos.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

VALIDACIÓN DE INGRESO O LECTURA: proceso por el que se verifica, y eventualmente


rechaza, un ingreso de datos, buscando detectar posibles errores. Para validar un
ingreso podemos escribir una o varias condiciones que nos permitan testear tales
datos.
FUNCIONES DE CASTEO
a.isnumeric(): evalúa si el dato ingresado es un número.
a.isdecimal(): evalúa si el dato ingresado es un decimal.
a.isdigit(): evalúa si el dato ingresado es de caracteres dígitos.
SENTENCIA try/except: try toma recaudos al momento de realizar una acción; si esta tiene
éxito, el programa prosigue su curso, sin embargo, si en el intento emerge un error, la
sentencia lo captura y lo trata de una manera conveniente en la cláusula except. Sintaxis:
try:
-
except [código_de_error, código_de_error]:
-

Nota: - El manejo de excepciones está dentro de un bucle. Si se rechaza un ingreso, debemos


permitir otro. Por eso solo salimos del bucle cuando se puede ejecutar completamente
el bloque vinculado a la cláusula try.
- Los códigos de error que se registran en la lista del except son optativos. Si no se
pone ninguno, capturará todos los errores que puedan producirse. Si se decide actuar
solo con algunos, hay que consignarlos. Si son más de uno, van separados por coma.

UNIDAD 6: bibliotecas de Python.


PANDAS: biblioteca de Python que se utiliza para el análisis y la manipulación de datos en
bruto; puede ayudar a los usuarios a limpiar, transformar y analizar datos de una manera
rápida y eficiente.
DATAFRAME: estructura de datos principal en Pandas; es una tabla de datos bidimensional
que se compone de filas y columnas, y se asemeja a una hoja de cálculo de Excel. Los
DataFrames son útiles para almacenar y manipular grandes cantidades de datos y
proporcionan una gran cantidad de funcionalidades, como la selección y filtrado de datos, la
agregación de datos y la realización de operaciones matemáticas y estadísticas.
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

IMPORTACIÓN DE PANDAS: agrego un alias con el que voy a llamar a la biblioteca en mi


programa, por ejemplo: pd. Sintaxis:
import pandas as pd
MOSTRAR VERSIÓN DE PANDAS QUE IMPORTAMOS: sirve para saber que funcionalidad tiene
la biblioteca que estamos utilizando. Sintaxis:
pd._version_
MÉTODOS DE PANDAS
pd.DataFrame(data, index=labels): crea un DataFrame a partir de un diccionario “data” y
usando una lista “labels” como índice.
Nota: guardamos el DataFrame en una variable con alias df para su uso.
df.info(): devuelve información sobre el DataFrame incluidos sus índices y columnas, valores
no nulos y uso de memoria.
df.describe(): devuelve una serie con un resumen descriptivo que incluye el número de datos,
su suma, el mínimo, el máximo, la media, la desviación típica y los cuartiles (estadísticas).
df.head(n): devuelve las primeras n filas del DataFrame df. En el caso de no especificarlo,
devuelve las primeras 5.
df.iloc[desde_donde:hasta_donde]: devuelve las filas indicadas entre tales posiciones.
df.iloc[filas, columnas]: devuelve un DataFrame con todos los elementos de las filas de la lista
filas y las columnas de la lista columnas.
df.loc[fila1:fila3, [columna1, columna2]]: muestra las filas del rango especificado y las
columnas especificadas. Si no se especifica las filas, muestra todas por defecto.
df[[columna1, columna2]]: muestra todas las filas de las columnas especificadas.
df.loc[df.index[[fila1, fila2, fila3], [columna1, columna2]]: muestra las filas y columnas
especificadas.
df.loc[fila, columna] = n: cambia el valor de una celda.
df[df[condición] > n]: filtra los elementos de la tabla según la condición dentro de los
corchetes.
df[(df[condición1] > n) & (df[condición2] > x)]: concatena condiciones.
df[df[columna].isnull()]: selecciona las filas de la columna especificada en donde NO hay
datos, es decir que su valor sea nulo (None o NaN).
df[df[columna].isnull()==False]: selecciona las filas de la columna especificada en donde SÍ
hay datos.
df[df[columna].between(a, b)]: selecciona las filas de la columna especificada que tengan
datos que se encuentren en el rango [a, b] (incluye los extremos).
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

Nota: - las condiciones para pandas son and=&, or=|, not=~.


- las condiciones siempre van entre [] aplicadas al DataFrame.
- None es la variable nula de un string (u objeto) y NaN es la variable nula de un número.
df[columna].sum(): suma todos los valores de la columna especificada.
df[columna].mean(): calcula el promedio con los elementos de la columna.
df.groupby(columna): agrupa los valores de la columna especificadas.
df.loc[n]= obtener información del elemento n.
df.loc['k'] = ['a', 1, 2, 'b']: agrega un nueva fila “k” con los valores que queramos para cada
columna.
df.drop('k'): elimina la fila “k”.
df.drop(len(df)-1)= elimina la última fila del DataFrame.
df[columna].value_counts(): devuelve cuántas veces aparece cada valor único en la columna
especificada.
df.sort_values(by=[columna1, columna2], ascending=[False, True]): ordena la tabla con el
criterio que se le pida. En el parámetro by ponemos una lista con las columnas que queremos
usar para ordenar y en ascending ponemos una lista de booleanos donde indicamos si
queremos que el ordenamiento de la lista anterior sea ascendente (True) o descendente
(False).
df[columna].map({'valor1': True, 'valor2': False}): transforma los valores de columna entera;
le pasamos un diccionario con elementos del tipo {valor_viejo: valor_nuevo}.
df[columna].replace('valor_viejo', 'valor_nuevo'): otro modo de reemplazar valores.
MATPLOTLIB: biblioteca de Python para crear gráficos en 2D (plots). Provee una forma rápida
de graficar datos en varios formatos de alta calidad que pueden ser compartidos y/o
publicados.
PYPLOT: graficador en Python; proporciona una interfaz a la biblioteca de Matplotlib.
IMPORTACIÓN: sintaxis:
from matplotlib import pyplot as plt
MÉTODOS DE MATPLOTLIB
plt.plot(x, y): recibe 2 arreglos (del mismo tamaño) que representan una serie de puntos en
el plano cartesiano (x, y); luego une todos estos puntos con líneas, formando el gráfico de la
función.
plt.scatter(x, y): recibe parámetros ídem a plot; crea un gráfico de dispersión (dibuja puntos
en el plano, sin unirlos con líneas).
plt.subplot(1, 1, 1): crea un nuevo subplot (en este caso, en una grilla de 1x1).
Resumen Pensamiento Computacional UBA XXI – Chiara López Angelini

plt.xlabel(etiqueta): recibe un string que se usará como etiqueta del eje X.


plt.ylabel(etiqueta): recibe un string que se usará como etiqueta del eje Y.
plt.title(título): recibe un string que se usará como título del gráfico.
plt.bar(etiquetas, altura): crea un gráfico de barras; primero recibe un arreglo con las
etiquetas de las barras que se van a mostrar y después otro arreglo con la altura de cada una
de estas barras.
plt.pie(proporción, labels= etiquetas, autopct=”%1.1f%%”): crea un gráfico de torta; puede
solamente recibir un arreglo de números pero es útil también saber que simboliza cada parte,
para eso se usa el parámetro labels que le asigna una etiqueta a cada porción; autopct
mostrará el porcentaje de cada porción.
plt.show():crea la imagen con todos los gráficos definidos anteriormente.

CAMBIAR PROPIEDADES POR DEFECTO DE LOS GRÁFICOS


plt.figure(figsize=(pulgada_x, pulgada_y), dpi=puntos_por_pulgada): cambia el tamaño de
la imagen.
plt.plot(x, y, color="color", linewidth=ancho, linestyle= ‘-‘/ ‘dotted’): cambia el color, el
ancho y el tipo de línea.
plt.xlim(x.min() * valor, x.max() * valor): cambia el rango o los límites del eje x. Idem para y.
plt.xticks([valores]): muestra únicamente los valores especificados en las etiquetas de x.
Idem para y.
ax = plt.gca(): toma el eje actual (y lo guarda en una variable).
ax.spines['right', 'left', 'top', 'bottom'].set_color('none'): selecciona una línea y le cambia su
color (en este caso la deja “trasparente”).
ax.spines['right', 'left', 'top', 'bottom'].set_position((‘data’,0)): selecciona una línea y la
traslada a otra coordenada.
plt.legend(loc='posición'): coloca una leyenda a modo de referencias del gráfico en la
posición especificada.
plt.annotate(‘anotación’, xy= ‘posición’, xytext= ‘posición’): añade anotaciones al gráfico.

También podría gustarte