From e81137474f102d71fe0b25307bcf88810c1b2d43 Mon Sep 17 00:00:00 2001 From: Ekaitz Zarraga Date: Wed, 4 Mar 2020 13:41:02 +0100 Subject: New arrangement for multilanguage and metadata support --- src/02_datos.md | 662 -------------------------------------------------------- 1 file changed, 662 deletions(-) delete mode 100644 src/02_datos.md (limited to 'src/02_datos.md') diff --git a/src/02_datos.md b/src/02_datos.md deleted file mode 100644 index 1eb2f1a..0000000 --- a/src/02_datos.md +++ /dev/null @@ -1,662 +0,0 @@ -# Trabajando con datos - -Programar es, principalmente, tratar con datos y los datos no son más que -piezas de información que se estructura de una forma concreta. - -## Nombrando datos - -Los datos se almacenan en la memoria principal de la computadora. Puedes -imaginarte la memoria como un conjunto de cajas identificadas con direcciones, -como si fuesen los buzones de un portal con muchos pisos. Para utilizar datos, -éstos se guardan en los diferentes cajones y se van extrayendo y actualizando. -La importancia de nombrar las cosas es evidente, si no guardamos un listado de -dónde hemos guardado qué no podemos recuperar los datos que hemos creado. - -Al igual que en las matemáticas se utilizan los símbolos para representar -posibles valores y simplificar los procesos, en los lenguajes de programación -se utilizan símbolos para definir referencias a los datos y así poder referirse -a los mismos datos por su nombre sin tener que introducir su contenido -constantemente. Los lenguajes de programación aportan al programador la -facilidad de poder crear sus propios nombres para no tener que usar las -direcciones propias de la memoria, que normalmente son número sin más -significado que su posición, la mayor parte de las veces permitiendo al -programador abstraerse de estos detalles internos. - -En python existe un operador para declarar símbolos que hacen referencia a un -valor: el operador `=`. Este operador enlaza el valor de la derecha con el -nombre de la izquierda. Con un ejemplo es más fácil de comprender. Probando en -la REPL: - -``` python ->>> a = 10 ->>> a -10 ->>> a + 1 -11 -``` - -Como ves, en el primer paso se asocia el valor `10` al identificador `a` y más -adelante se puede utilizar `a` para hacer referencia al valor enlazado. - -Las referencias han de ser declaradas antes de usarse, si no, el intérprete no -las conoce y lanza una excepción: - -``` python ->>> b -Traceback (most recent call last): - File "", line 1, in - b -NameError: name 'b' is not defined -``` - -> NOTA: Los nombres para poder ser interpretados correctamente por python deben -> cumplir unas normas estrictas: -> -> - No pueden tener espacios. -> - Sólo pueden estar formados por combinaciones de letras, números y el -> símbolo `_`. -> - No pueden empezar por un dígito. - -### Todo es una referencia - -El comportamiento de estas referencias puede no ser intuitivo si vienes de -otros lenguajes. El operador `=` enlaza, como se dice anteriormente, un nombre -a un valor concreto, pero eso no significa que copie su contenido. En python -los valores y los nombres son conceptos independientes. Los valores ocupan su -lugar en la memoria y los nombres hacen referencia a dónde se encuentran estos -valores. Es muy común tener muchas referencias a la misma estructura de datos y -transformar su contenido desde todas las referencias. - -A nivel técnico, lo que en python se conoce como variable y en este documento -hemos hecho el esfuerzo de llamar referencia es lo que en otros lenguajes se -conoce como un puntero y la labor del operador `=` es la de asignar el puntero -a la dirección donde se encuentran los datos a los que debe apuntar. - -Volviendo a la metáfora de los buzones, en lenguajes de más bajo nivel como C -estás obligado a seleccionar qué buzón vas a utilizar para introducir cada -dato. Por lo que si cambia el tipo de dato a gestionar, puede que el buzón se -quede pequeño o que los datos se interpreten de forma incorrecta. Sin embargo, -python guarda las referencias de forma independiente a los valores y adecua el -número de buzones en uso al tamaño de los datos de los que dispones -reordenando, si es necesario, la estructura completa de valores y referencias. -A este concepto se le conoce como gestión automática de memoria (*automatic -memory management*). - -En resumen, las referencias en python son únicamente un recordatorio que sirve -para poder acceder a un valor. Esto cobra importancia más adelante, y no vamos -a rehuir la responsabilidad de enfangarnos en ello. - -## Tipos - -Tal y como aclaraba el texto de la introducción, python tiene un sistema de -tipos dinámico (*dynamic type system*). Lo que significa que gestiona los tipos -de forma automática, permitiendo a los nombres hacer referencia a diferentes -tipos de valor durante la ejecución del programa a diferencia de otros -lenguajes como, por ejemplo, C, donde el tipo de las variables debe ser -declarado de antemano y no puede cambiarse. - -Esto es posible debido al fenómeno explicado en el apartado anterior por un -lado, y, por el otro, a que los datos de python son un poco más complejos que -en otros lenguajes y guardan una pequeña nota que indica cómo deben ser -interpretados. - -Si en algún momento se le pide a python que asigne un valor de un tipo distinto -al que una referencia tenía no habrá problemas porque es el propio dato quien -guarda la información suficiente para saber cómo entenderlo. Las referencias -sólo almacenan dónde se guarda este dato. - -> NOTA: Seguramente te habrás dado cuenta de que el funcionamiento de python es -> más ineficiente que el de C o lenguajes similares pero mucho más flexible. Y -> así es. A la hora de elegir el lenguaje debemos valorar cuál nos interesa -> más para la labor que vamos a realizar. - -### Tipos simples - -Hemos denominado tipos simples a los que no empaquetan más de un valor -internamente. En otros lenguajes o contextos se les conoce como escalares. - - -#### La nada - -La nada en python se representa con el valor `None` y es útil en innumerables -ocasiones. - -#### Boolean - -Los valores booleanos expresan *verdad* (`True`) o *mentira* (`False`) y sirven -para gestionar lógica desde esos términos. Mas adelante los veremos en acción. - -#### Integer - -Los Integer, números enteros en inglés, ya han aparecido anteriormente. Para -usar un número entero puedes introducirlo tal cual. Recuerda que hay enteros -positivos y negativos. El símbolo para marcar números negativos en python es el -`-`, que coincide con el operador de la resta. - -``` python ->>> 14 -14 ->>> -900 --900 ->>> -``` - -Los números enteros también pueden expresarse en otras bases, como en -hexadecimal. Dependiendo de la aplicación en la que te encuentres igual -necesitas mirarlo más en detalle: - -``` python ->>> 0x10 -16 -``` - -#### Float - -Los números Float, o de coma flotante, son números no-enteros. Ten cuidado -con ellos porque la coma flotante es peculiar. - -El nombre coma flotante viene de que la coma no siempre se mantiene con la -misma precisión. En realidad estos números se guardan como los números en -notación científica (2,997E8 m/s para la velocidad de la luz, por ejemplo). -La notación científica siempre implica tener una coma, pero cuya posición se -varía con el exponente posterior. - -El modo de almacenamiento de los números de coma flotante es muy similar a la -notación científica: se almacenan dos valores de tamaño fijo, la *mantisa* y el -*exponente* para, de este modo, poder ajustar la precisión en función del -tamaño del número almacenado. No es lo mismo expresar la velocidad de la luz, -2,997E8 m/s, que la longitud de onda del color rojo, 6,250E-7 m, ambos -valores tienen un tamaño muy distinto, pero usando dos *mantisas* de tamaño -similar, cuatro dígitos (2997 y 6250), y dos exponentes de tamaño similar, un -dígito (8 y -7), hemos expresado valores muy diferentes, ambos con la misma -precisión relativa. - -El problema viene cuando nos apetece mezclarlos, por ejemplo, sumándolos. -Imagina que tienes dos valores de esas dimensiones, uno de millones y otro de -millonésimas partes de la unidad, y los quieres sumar entre ellos. Si sólo -tienes una mantisa limitada para representarlos, la suma resultará en el -redondeo de ambos a la precisión que tienes disponible. Es decir: el resultado -será el valor grande y el pequeño se perderá en el redondeo. - -Aunque en este caso la suma es precisa, si tratas con un billón de valores -pequeños y uno grande y quieres obtener la suma de todos y los sumas en parejas -siempre con el grande, en cada suma se descartará el valor pequeño en el -redondeo y el resultado total será el valor grande que tenías. Sin embargo, si -sumas los valores de tamaño similar entre ellos primero, obtendrás un valor -suficientemente grande como para alterar el resultado de la suma contra el -valor grande al final, dando así un resultado distinto. Te recomiendo entonces, -que si te encuentras en una situación como ésta tengas cuidado y, por ejemplo, -ordenes los números de menor a mayor antes de sumarlos, para obtener una suma -de buena precisión. - -En realidad, el exponente en el caso de python (y en casi todos los demás -lenguajes) no está elevando un 10 a la enésima potencia, si no que lo hace con -un 2. Por lo que lo expresado anteriormente es un poco distinto. Esto provoca -que algunos números de coma flotante no sean tan redondos como deberían (por -ejemplo, `2.999999999`, cuando debería ser `3.0`) y compararlos entre ellos -puede ser desastroso. Para evitarlo, te recomiendo que *siempre* redondees los -valores a una precisión que puedas controlar. - -Aunque realmente es algo más complejo[^1], lo que sabes ahora te evitará -problemas en el futuro, sobre todo cuando analices datos, uno de los sectores -donde python se usa de forma extensiva. - -[^1]: https://en.wikipedia.org/wiki/Floating-point_arithmetic - -Declarar números de coma flotante es natural porque usa una sintaxis a la que -estamos acostumbrados: - -``` python ->>> 1E10 -10000000000.0 ->>> 1.0 -1.0 ->>> 0.2E10 -2000000000.0 ->>> -``` - - -#### Complex - -Python soporta números complejos y los expresa utilizando la letra `j`. Como -suponen un caso bastante concreto no los analizaremos en detalle. Pero tienes -disponible la documentación de python para lo que quieras. - -``` python ->>> 1-23j -(1-23j) -``` - -#### String - -Un String es una cadena de caracteres. Los Strings en python son, a diferencia -de en otros lenguajes, un escalar, al contrario de lo que la primera definición -que hemos expresado puede hacernos pensar. En python los Strings no son un -conjunto de caracteres alfanuméricos sueltos que se comportan como un valor, es -al revés. El concepto de carácter no existe y ha de expresarse con un String de -longitud 1. - -Los strings se expresan rodeando texto con comillas dobles, `"`, o simples `'` -(el símbolo del apóstrofe). - -``` python ->>> "Hola" -'Hola' ->>> 'Hola' -'Hola' -``` - -El hecho de que haya dos opciones para delimitar los strings facilita el -etiquetado como string de valores que contienen las propias comillas en su -contenido. También puede utilizarse la contrabarra `\` para cancelar la acción -de las comillas. - - -``` python ->>> "Tiene un apóstrofe: Luke's" -"Tiene un apóstrofe: Luke's" ->>> 'Tiene un apóstrofe: Luke's' -SyntaxError: invalid syntax ->>> 'Tiene un apóstrofe: Luke\'s' -"Tiene un apóstrofe: Luke's" -``` - -> NOTA: la contrabarra sirve para introducir caracteres especiales o caracteres -> de escape[^2]: `\n` salto de línea, `\t` tabulador, etc. Que son una herencia -> de los tiempos de las máquinas de escribir, pero son aún útiles y muy usados. -> Para expresar la propia contrabarra ha de escaparse a sí misma con otra -> contrabarra para que no evalúe el siguiente caracter como un caracter de -> escape.: `\\`. - -[^2]: https://en.wikipedia.org/wiki/Escape_character - - -### Tipos compuestos - -Hemos denominado tipos compuestos a los que pueden incluir diferentes -combinaciones de tipos simples. Estos tipos dejan de ser escalares y se -comportan como vectores o conjuntos de datos. Estos tipos de dato pueden -contenerse a sí mismos, por lo que pueden crear estructuras anidadas complejas. - -#### Tuple - -Las tuplas o *tuple* en inglés son el tipo compuesto más sencillo en python. -Las tuplas definen un conjunto de valores de cualquier tipo. - -Se declaran utilizando paréntesis añadiendo sus elementos separados por comas. -Y se accede a sus contenidos utilizando los corchetes e introduciendo el índice -del elemento que se quiere extraer. - -``` python ->>> (2, 3, "HOLA") -(2, 3, 'HOLA') ->>> (2, 3, "HOLA")[0] -2 -``` -En python los índices comienzan en `0`. - -#### List - -Las listas o *list* son muy similares a las tuplas, pero son algo más complejas -porque pueden alterarse así mismas. A diferencia de todos los tipos que hemos -visto hasta ahora, tanto las listas como los diccionarios que veremos a -continuación son mutables. Esto significa que puede transformarse su valor, más -adelante trataremos esto en detalle. - -De momento, recuerda que las listas se construyen de forma similar a las -tuplas, pero utilizando corchetes en lugar de paréntesis. La forma de acceder a -los índices es idéntica. - -``` python ->>> [2, 3, "HOLA"] -[2, 3, 'HOLA'] ->>> [2, 3, "HOLA"][0] -2 -``` - -#### Dictionary - -Los diccionarios o *dictionary* son un tipo de dato similar a los dos -anteriores, pero que en lugar de utilizar índices basados en la posición de sus -elementos usan claves arbitrarias definidas por el usuario. - -Además, los diccionarios no están ordenados así que no se puede suponer que las -claves siempre van a estar en el orden en el que se introducen. - -Para declarar diccionarios es necesario indicar qué claves se quieren usar. Las -claves son de tipo string. En el caso de los diccionarios, además, se utilizan -llaves para definirlos. El acceso a sus valores se realiza con los corchetes, -del mismo modo que en las listas, pero es necesario seleccionar la clave para -acceder. - -Los diccionarios, al igual que las listas, son mutables. Como veremos en -seguida. - -``` python ->>> {"nombre": "Guido", "apellido": "Van Rossum", "popularidad": 8.0} -{'nombre': 'Guido', 'apellido': 'Van Rossum', 'popularidad': 8.0} ->>> {"nombre": "Guido", "apellido": "Van Rossum", "popularidad": 8.0}["popularidad"] -8.0 -``` - -#### Set - -Los *sets* son muy similares a las listas y tuplas, pero con dos -peculiaridades: - -- Sus valores son únicos. No pueden repetirse. -- No están ordenados - -Estas dos características tan estrictas, lejos de ser limitantes, aportan una -mejora radical en su rendimiento. Buscar elementos en un set es extremadamente -eficiente y se usan principalmente para esa labor. - -Si quieres validar en algún momento que un valor pertenece a un conjunto de -valores, el set es el tipo de dato que estás buscando. - -Los sets se declaran también usando las llaves, como un diccionario, pero no -usan claves. - -``` python ->>> {"a", "b", 1} -{'a', 1, 'b'} -``` - -## Conversión - -Ahora que conoces los valores sé que quieres saber cómo cambiar de uno a otro. -Cómo leer un Integer desde un String etc. Python tiene funciones para construir -sus diferentes tipos de datos a partir de los diferentes inputs posibles. - -Aunque aún no sabes ejecutar funciones te adelanto cómo se hace con algunos -ejemplos: - -``` python ->>> bool(1) -True ->>> bool(0) -False ->>> int("hola") -Traceback (most recent call last): - File "", line 1, in - int("hola") -ValueError: invalid literal for int() with base 10: 'hola' ->>> int("10") -10 ->>> float(10) -10.0 ->>> complex(19) -(19+0j) ->>> str(10) -'10' ->>> tuple([1,2,3]) -(1, 2, 3) ->>> list((1,2,3)) -[1, 2, 3] ->>> dict((("a", 1),("b", 2))) -{'a': 1, 'b': 2} ->>> set([1,2,2,3,4,4,4,4,4]) -{1, 2, 3, 4} -``` - -Los propios nombres de las funciones son bastante representativos de a qué tipo -convierten. Si quieres saber más puedes ejecutar `help(nombre)` y ver qué te -cuenta la ayuda. - -> NOTA: Fíjate que si conviertes una secuencia de valores repetidos a *set* -> únicamente almacena los que no se repiten. Es uno de los usos más comunes que -> tienen. - - -## Operadores - -Ahora que sabes el contexto en el que vas a jugar, necesitas poder alterar los -datos. - -Existen operadores básicos que te permiten transformar los datos, algunos ya -los has visto antes, el operador `=`, que sirve para nombrar cosas, la suma -(`+`) o la resta (`-`), pero hay otros. - -``` python ->>> 1 + 1 -2 ->>> 10 - 9 -1 ->>> 10 ** 2 -100 ->>> -``` - -Los siguientes apartados muestran algunos operadores que es interesante -memorizar. - - -### Pruebas de verdad - -Las pruebas de verdad generan un valor booleano desde una pareja de valores. A -continuación una lista de las pruebas de verdad con unos ejemplos: - -| operator | meaning | -|---|---| -| < | strictly less than | -| <= | less than or equal | -| > | strictly greater than | -| >= | greater than or equal | -| == | equal | -| != | not equal | -| is | object identity | -| is not | negated object identity | -| in | containment test | -| not in | negated containment test | - -``` python ->>> 1 > 1 -False ->>> 1 >= 1 -True ->>> 1 not in (0, 2, 3) -True -``` - -Aunque en otros lenguajes no es posible, la notación matemática habitual se -puede utilizar en python concatenando pruebas de verdad: - -``` python ->>> x = 1.2 ->>> 1 < x < 2 -True -``` - -### Operadores lógicos - -Los operadores lógicos mezclan booleanos y son muy importantes, sobre todo para -combinar las pruebas de verdad. - -| operator | meaning | -|---|---| -| and | logical and, returns True if both True, False otherwise | -| or | logical or, returns True if any True, False otherwise | -| not | logical not, returns True if False, True otherwise | - -La mayor parte de los operadores son binarios (como la suma), necesitan dos -valores y devuelven otro, pero existe al menos una excepción que funciona con -un único valor. El operador `not` sirve para darle la vuelta a un Booleano. - -``` python ->>> not True -False ->>> True and True -True ->>> False and True -False ->>> False or True -True ->>> 1 > 0 and 2 > 1 -True -``` - -### Matemáticos - -Los operadores matemáticos transforman números entre ellos. Casi todos son -conocidos por su operación matemática. - -| operator | meaning | -|---|---| -| + | addition | -| - | subtraction or negative | -| `*` | multiplication | -| / | division | -| `**` | power | -| % | remainder | - -### Ternary operator - -Existe además un operador que puede usar tres parámetros, el *inline-if* (*if -en línea*), o *ternary operator*[^ternary]. El *ternary operator* se comporta -así: - -``` python ->>> 1 if 1 > 9 else 9 -9 ->>> 1 if 1 < 9 else 9 -1 -``` - -[^ternary]: - - -### Operación en función del tipo - -Python simplifica muchas tareas transformando el comportamiento de los -operadores en función del tipo de dato sobre el que trabajan. Los operadores -matemáticos están preparados para trabajar sobre números (de cualquier tipo) -pero la verdad es que algunos pueden ejecutarse sobre otros formatos. Por -ejemplo: - -``` python ->>> "a" + "a" -'aa' ->>> [1,2] + [3,4] -[1, 2, 3, 4] -``` - -Esto se debe a que la funcionalidad del operador `+` ha sido diseñada para -operar de forma especial en Strings y en Listas, haciendo una concatenación. - -En el futuro, cuando aprendas a diseñar tus propios tipos podrás hacer que los -operadores les afecten de forma especial, tal y como pasa aquí. - -### Precedencia - -La precedencia en python es muy similar a la matemática y usa las mismas reglas -para marcarla de forma explícita. Recuerda, en matemáticas se utilizan los -paréntesis para esto. - -Los operadores siempre trabajan con sus correspondientes valores y python los -resuelve de forma ordenada. Si generas una operación muy compleja, python la -irá desgranando paso a paso y resolviendo las parejas una a una, cuanto más te -acostumbres a hacerlo en tu mente menos errores cometerás. - -``` python ->>> 8 + 7 * 10 == (8 + 7) * 10 -False ->>> 8 + 7 * 10 -78 ->>> (8 + 7) * 10 -150 ->>> 78 == 150 -False -``` - -## Mutabilidad - -Ya adelantamos que el operador `=` sirve para nombrar cosas. Ahora vamos a -combinar esa propiedad con la mutabilidad, o la propiedad de las cosas de -alterarse a sí mismas. Empecemos con un ejemplo: - -``` python ->>> a = 10 ->>> b = a + 10 ->>> b -20 ->>> a = b ->>> a -20 -``` - -En este ejemplo hacemos que `a` haga referencia al valor 10, y después creamos -`b` a partir de `a` y otro 10. Gracias a la precedencia, primero se resuelve el -lado derecho completo obteniendo un 20 y después se asigna la referencia `b` a -ese valor. - -Si queremos, después podemos reasignar el símbolo `a` a otro valor nuevo, en -este caso al que hacer referencia `b` que es 20. - -En este primer ejemplo, ningún valor está siendo alterado, si te fijas, sólo -estamos creando nuevos valores y cambiando las referencias a éstos. - -Con los datos mutables podemos alterar los valores. Lo vemos con otro ejemplo: - -``` python ->>> a = {} ->>> b = a ->>> a["cambio"] = "hola!" ->>> b -{'cambio': 'hola!'} -``` - -Primero creamos un diccionario vacío y le asignamos la referencia `a`. Después -le asignamos la referencia `b` a quien referenciaba `a`, es decir, al -diccionario vacío. Ambas referencias apuntan al mismo dato. Si después usamos -alguna alteración en el diccionario `a` como asignarle una nueva clave, `b`, -que hace referencia al mismo diccionario, también ve los cambios. Esto mismo -podría hacerse si se tratara de listas, ya que tienen la capacidad de alterarse -a sí mismas, pero nunca podría hacerse en tuplas, porque son inmutables. - -> NOTA: Los diccionarios y las listas soportan un montón de funciones y -> alteraciones, no se mencionan en este apartado porque nunca terminaría. Se -> dejan para el futuro y para los ejemplos que se verán durante el documento. - -Si intentásemos un ejemplo similar en tupla, no nos dejaría: - -``` python ->>> a = () ->>> b = a ->>> a[0] = 1 -Traceback (most recent call last): - File "", line 1, in - a[0] = 1 -TypeError: 'tuple' object does not support item assignment -``` - -Ten cuidado cuando trates con elementos mutables, sobre todo si tienen muchas -referencias, porque puede que estés alterando los valores en lugares que no te -interesa. Para evitar este tipo de problemas, puedes generar copias de los -objetos, pero el proceso es poco eficiente y tedioso. - -En este segundo caso, creamos una copia de `a` para que `b` sea independiente -de los cambios que ocurran en ésta. Aquí ya no estamos haciendo referencia -desde `b` a los datos que había en `a`, sino a una copia de éstos, almacenada -en otro lugar. - -``` python ->>> a = {} ->>> b = dict(a) ->>> a["cambio"] = "hola!" ->>> b -{} ->>> a -{'cambio': 'hola!'} -``` - -## Lo que has aprendido - -En este apartado has conocido los tipos fundamentales de python y cómo -convertir de uno a otro. Además, al conocer los operadores y su funcionamiento -ya eres más o menos capaz de usar python como una calculadora. - -Además, has tenido ocasión de entender de forma superficial varios conceptos -avanzados como el manejo automático de memoria, el tipado dinámico y los -números de coma flotante, que son muy interesantes a la hora de trabajar porque -te permiten comprender la realidad que tienes a tu alrededor. -- cgit v1.2.3