summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/00_meta.md12
-rw-r--r--src/01_intro.md207
-rw-r--r--src/02_datos.md662
-rw-r--r--src/03_estructura.md550
-rw-r--r--src/04_funciones.md654
-rw-r--r--src/05_oop.md1093
-rw-r--r--src/06_ejec_mod.md271
-rw-r--r--src/07_install.md243
-rw-r--r--src/08_stdlib.md372
-rw-r--r--src/09_extralib.md162
-rw-r--r--src/10_closing_words.md123
-rw-r--r--src/A_devtools.md90
-rw-r--r--src/Z_license.md337
13 files changed, 0 insertions, 4776 deletions
diff --git a/src/00_meta.md b/src/00_meta.md
deleted file mode 100644
index 7dfbcd0..0000000
--- a/src/00_meta.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-links-as-notes: true
-toc: true
-title: Python
-subtitle:
-author: Ekaitz Zárraga
-author-meta: Ekaitz Zárraga
-lang: es-ES
-polyglossia-lang:
- name: spanish
-license: CC-BY-SA
----
diff --git a/src/01_intro.md b/src/01_intro.md
deleted file mode 100644
index b2a1752..0000000
--- a/src/01_intro.md
+++ /dev/null
@@ -1,207 +0,0 @@
-# Introducción
-
-Python es un lenguaje de programación de alto nivel orientado al uso general.
-Fue creado por Guido Van Rossum y publicado en 1991. La filosofía de python
-hace hincapié en la limpieza y la legibilidad del código fuente con una
-sintaxis que facilita expresar conceptos en menos líneas de código que en otros
-lenguajes.
-
-Python es un lenguaje de tipado dinámico y gestión de memoria automática.
-Soporta múltiples paradigmas de programación, incluyendo la programación
-orientada a objetos, imperativa, funcional y procedural e incluye una extensa
-librería estándar.
-
-Pronto entenderás lo que todo esto significa, pero antes hay que instalar las
-herramientas necesarias y trastear con ellas.
-
-## Instalación
-
-Para trabajar con python se necesita:
-
-- python3: el intérprete de python, en su versión 3. Verás que hay muchas
- subversiones. Este documento cubre cualquiera de ellas.
-
-- pip: el gestor de paquetería de python. También se conoce como pip3 para
- diferenciarlo del pip de python2.
-
-Nosotros añadiremos un par de amigos a la lista:
-
-- idle3: un editor de código python muy sencillo. Usaremos este porque
- representa el ecosistema de forma muy simple. En el futuro, te recomiendo
- usar algún otro editor más avanzado.
-
-- pipenv: el estándar de facto para gestionar entornos virtuales en python3.
- Luego entenderás qué es eso.
-
-
-### Instalación en distribuciones de Linux
-
-La instalación puede realizarse desde el gestor de paquetes habitual, ya que
-python suele distribuirse en todos los repositorios de paquetes.
-
-En las distribuciones que usan el sistema de paquetes de Debian, puede
-instalarse desde la terminal con el siguiente comando:
-
-``` bash
-sudo apt-get install python3 python3-pip idle3
-```
-
-### Instalación en otros sistemas
-
-Como siempre instalar en otros sistemas es más farragoso. Pero no es demasiado
-difícil en este caso. La instalación puede realizarse con una descarga desde la
-página web oficial de python:
-
-<https://python.org/downloads/>
-
-Una vez ahí seleccionar la versión necesaria, descargar el instalador y seguir
-las instrucciones de éste. Recuerda seleccionar **instalar pip** entre las
-opciones y activar la casilla de **añadir python al PATH**, que permitirá que
-que ejecutes programas de python sin problemas. También puedes añadir
-**IDLE**, el programa que sirve para editar el código, pero te recuerdo que
-es un programa muy sencillo, que nos servirá para entender lo básico del
-entorno sin ocultarnos el proceso, pero que más adelante podrás utilizar otros
-editores que simplifiquen tareas.
-
-
-## Admira el paisaje
-
-Una vez que has instalado python, es interesante ver lo que eso significa.
-Python es un intérprete de código fuente del lenguaje del mismo nombre.
-Concretamente, la que has instalado es una de las posibles implementaciones (la
-implementación de referencia, en este caso) de este intérprete, conocida como
-CPython, en su versión 3. Existen otras implementaciones, cada una con sus
-peculiaridades, pero ésta es la principal y la más usada.
-
-Como intérprete que es, python es capaz de leer un archivo escrito en su
-lenguaje y ejecutar sus órdenes en tu computadora. Ésta es principalmente su
-labor. Sin embargo, también es capaz de realizar esta operación de forma
-interactiva recibiendo las órdenes una por una y devolviendo el resultado de su
-ejecución como respuesta. Este proceso se conoce como REPL, acrónimo de
-read-eval-print-loop (lee-evalúa-imprime-repite), aunque en otros lugares se le
-conoce como la shell de python.
-
-> NOTA: La shell de python (o REPL) y la shell del sistema son cosas
-> diferentes. La shell de sistema también es un intérprete pero del lenguaje
-> que el sistema ha definido (Bash, PowerShell...) y no suele ser capaz de
-> entender python.
-
-Para acostumbrarte a la shell te propongo que abras IDLE. Lo primero que verás
-será parecido a esto:
-
-``` python
-Python 3.6.8 (default, Oct 7 2019, 12:59:55)
-[GCC 8.3.0] on linux
-Type "help", "copyright", "credits" or "license()" for more information.
->>>
-```
-
-Todo lo que escribas tras el símbolo `>>>` será interpretado como una orden y
-cuando la termines pulsando la tecla `ENTER` de tu teclado, recibirás el
-resultado de la ejecución de la orden insertada. El acrónimo REPL define el
-comportamiento de este ciclo a las mil maravillas:
-
-> NOTA: En este documento, siempre que veas el símbolo `>>>` significa que se
-> trata de un ejemplo ejecutado en la REPL. Si no lo ves, se tratará del
-> contenido de un archivo de código python ejecutado de forma independiente.
-
-1. Lee lo que introduces.
-2. Lo evalúa, obteniendo así un valor como resultado.
-3. Lo imprime.
-4. Repite el ciclo volviendo a leer.
-
-Por tanto, si introduces un valor directamente será devuelto:
-
-``` python
->>> 1
-1
-```
-
-Y si lo alteras, por ejemplo, con una operación matemática sencilla, devuelve
-el resultado correspondiente:
-
-``` python
->>> 2+2
-4
-```
-
-Como ejercicio te propongo lo siguiente:
-
-1. Abre la shell de python (puedes hacerlo en IDLE o desde la shell de sistema
- ejecutando `python` o `python3`).
-2. Entra en la ayuda interactiva. PISTA: el mensaje que aparece al abrir la
- REPL te dice cómo.
-3. Sal de la ayuda (descubre tú mismo cómo se hace).
-4. Ejecuta `import this` y lee el resultado.
-
-
-### Tu primer archivo de código fuente
-
-La REPL es interesante para probar y depurar tus programas (o para usarla como
-calculadora), pero es necesario grabar tus programas en ficheros si quieres
-poder volver a ejecutarlos más adelante o compartirlos.
-
-En IDLE puedes abrir un nuevo documento de código en el menú de archivo. Una
-vez lo tengas, como aún no sabes python puedes introducir lo siguiente:
-
-``` python
-nombre = "Guido"
-print("Hola, " + nombre)
-```
-
-Si guardas el fichero y pulsas `F5` (Ejecutar módulo), verás que en la pantalla
-de la REPL aparece el resultado `Hola, Guido`.
-
-Como ves, el resultado de ejecutar los ficheros de código fuente aparece en la
-shell, pero únicamente aparece lo que explícitamente le has pedido que imprima
-con la orden `print`.
-
-Para entender el valor de la REPL, te sugiero que vayas a su ventana, y justo
-después del resultado de la ejecución hagas lo siguiente:
-
-``` python
-Hola, Guido
->>> nombre
-'Guido'
->>>
-```
-
-La REPL conoce el valor `nombre` a pesar de que tu programa ha terminado de
-ejecutarse. Esto es interesante a la hora de probar y analizar el programa.
-
-
-### La vida real
-
-En realidad, los programas de producción no se ejecutan en una shell como la
-que IDLE nos brinda. IDLE sólo está facilitando nuestro trabajo como
-desarrolladores, como otros entornos de desarrollo hacen, cada uno a su manera.
-En producción el código se levantará ejecutando el intérprete de python
-directamente con nuestro programa como input. Por ejemplo en la shell **de
-sistema** usando el siguiente comando.
-
-``` bash
-python ejemplo.py
-```
-
-También es posible ejecutar los programas de python desde la interfaz gráfica,
-pero internamente el resultado será el mismo. Siempre que todo esté bien
-instalado y configurado, el sistema operativo despertará un intérprete de
-python que ejecute las órdenes del fichero.
-
-Es importante ser consciente de lo que ocurre bajo la alfombra, para así ser
-capaces de intervenir si encontramos errores.
-
-Más adelante, en la sección sobre módulos e importación volveremos aquí y
-estudiaremos cómo se cargan y se interpretan los programas.
-
-
-## Lo que has aprendido
-
-Has instalado python y te has acostumbrado a la herramienta (IDLE) que usarás
-durante tu aprendizaje. Has ejecutado tu primer fichero y encontrado la
-potencia de la REPL.
-
-Además, has abierto la ayuda y te has leído el Zen de Python, que pronto iremos
-desgranando juntos.
-
-Para ser una introducción no está nada mal.
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 "<pyshell#6>", line 1, in <module>
- 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 "<pyshell#27>", line 1, in <module>
- 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]: <https://en.wikipedia.org/wiki/%3F:#Python>
-
-
-### 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 "<pyshell#67>", line 1, in <module>
- 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.
diff --git a/src/03_estructura.md b/src/03_estructura.md
deleted file mode 100644
index 9d8cd5e..0000000
--- a/src/03_estructura.md
+++ /dev/null
@@ -1,550 +0,0 @@
-# Estructura del lenguaje
-
-Aunque ya sabes usar python de forma sencilla, aún no hemos tratado el
-comportamiento del lenguaje y cómo se estructura su sintaxis más allá de varios
-ejemplos sencillos y planos. En este apartado trataremos la estructura y las
-diferentes formas de controlar el flujo del programa.
-
-Como en la mayor parte de lenguajes de programación conocidos, python ejecuta
-las órdenes de arriba a abajo, línea por línea.
-
-Para demostrarlo, prueba a abrir un nuevo fichero, llenarlo con este contenido
-y ejecutarlo (`F5`).
-
-``` python
-print("Esta línea va primero")
-print("Esta línea va segundo")
-print("Esta línea va tercero")
-print("Esta línea va cuarto")
-```
-
-Verás que el resultado del programa es siempre el mismo para todas las veces
-que lo ejecutes y siempre salen los resultados en el mismo orden.
-
-Para poder alterar el orden de los comandos, o elegir en función de una
-condición cuales se ejecutan, python dispone de unas estructuras. Pero, antes
-de contarte cuales son, te adelanto su forma general. Normalmente se declaran
-en una línea terminada en `:` y su cuerpo se sangra hacia dentro. La sangría (o
-indentación si lo calcamos del inglés[^indent]) es lo que define dónde empieza
-o termina un *bloque* en python. Las líneas consecutivas sangradas al mismo
-nivel se consideran el mismo *bloque*.
-
-#### Bloques
-
-Los *bloques* de código son conjuntos de órdenes que pertenecen al mismo
-contexto. Sirven para delimitar zonas del programa, cuerpos de sentencias, etc.
-
-- Puedes comenzar nuevos bloques incrementando el nivel de sangría.
-- Los bloques pueden contener otros bloques.
-- Los bloques terminan cuando el sangrado disminuye.
-
-Es **muy importante** sangrar los bloques correctamente, usando una sangría
-coherente. Puedes usar dos espacios, el tabulador, cuatro espacios o lo que
-desees, pero elijas lo que elijas debe ser coherente en todo el documento.
-IDLE y los editores de código en general puede configurarse para usar una
-anchura de indentación concreta, que se insertará cuando pulses la tecla
-tabulador. El estándar de python es cuatro espacios.
-
-[^indent]: <https://en.wikipedia.org/wiki/Indentation_(typesetting)>
-
-## Sintaxis general
-
-La sintaxis de python es sencilla en su concepción pero ha ido complicándose a
-medida que el lenguaje ha ido creciendo. En este apartado únicamente se
-mencionan los puntos más comunes de la sintaxis, dejando para futuros apartados
-los detalles específicos de conceptos que aún no se han explicado.
-
-### Comentarios
-
-Los comentarios son *fundamentales* en el código fuente. El intérprete los
-ignora pero son primordiales para explicar detalles de nuestro código a otros
-programadores o a nosotros mismos en el futuro. Comentar bien el código fuente
-es un arte en sí mismo.
-
-Los comentarios en python se introducen con el símbolo `#`. Desde su aparición
-hasta el final de la línea se considera un comentario y python lo descarta.
-Pueden iniciarse a mitad de línea o en el inicio, tal y como se muestra a
-continuación:
-
-``` python
-# En este ejemplo se muestran comentarios, como este mismo
-print("Hola") # Esto es otro comentario
-```
-
-### Control de flujo
-
-Como ya se ha adelantado, es posible cambiar el orden de ejecución del programa
-en función de unas normas o evitar que el programa ejecute ciertas sentencias.
-A esto se le conoce como *control de flujo*.
-
-#### Condicionales
-
-Las condicionales son herramientas de *control de flujo* que permiten separar
-la ejecución en diferentes ramas en función de unas condiciones. En python sólo
-existe el condicional `if` («si», en castellano), aunque existen otras
-estructuras para conseguir el mismo efecto, no las trataremos aún.
-
-Ésta es la sintaxis del `if`:
-
-``` python
-if condición:
- # Este bloque se ejecuta si la condición se cumple
-elif condiciónN:
- # Este bloque (opcional) se ejecuta si las condiciones previas no se
- # cumplen y la condiciónN sí se cumple
-else:
- # Este bloque (opcional) se ejecuta si no se cumplen todas las condiciones
- # previas
-```
-
-Tal y como se muestra en el ejemplo, los bloques `elif` y el bloque `else` son
-opcionales. Es posible, y muy común además, hacer un `if` únicamente con el
-apartado inicial.
-
-Si te preguntas qué condiciones debes usar, es tan simple como usar sentencias
-de python cuyo resultado sea `True` o `False`. Cuando la sentencia resulte en
-un `True` la condición se cumplirá y el bloque interior se ejecutará.
-
-En resumen, el *if* ejecuta el bloque *si* la condición se cumple.
-
-#### Bucles
-
-Existen dos tipos de sentencia para hacer repeticiones en python, ambas son
-similares al `if`, pero en lugar de elegir si una pieza de código se ejecuta o
-no, lo que deciden es si es necesario repetirla en función de una condición.
-
-##### While
-
-El `while` («mientras que») es la más sencilla de estas estructuras, y la menos
-usada.
-
-``` python
-while condición:
- # Este bloque se ejecutará siempre que la condición se considere verdadera
-```
-
-El `while` comprueba la condición en primer lugar, si resulta en `True` ejecuta
-el bloque interno y vuelve a comprobar la condición. Si es `True`, ejecuta el
-bloque de nuevo, y así sucesivamente.
-
-Es decir, ejecuta el bloque *mientras que* la condición se cumple.
-
-##### For
-
-Los bucles *for* («para») son los más complejos y más usados,
-
-``` python
-for preparación:
- # Bloque a repetir si la preparación funciona con el contexto creado por la
- # preparación
-else:
- # Bloque (opcional) a ejecutar si el bloque cuerpo no termina de forma
- # abrupta con un `break`
-```
-
-No te preocupes ahora mismo por el `else`, ya que se suele considerar python
-avanzado y no suele usarse. Más adelante en este capítulo analizaremos un
-ejemplo.
-
-En lo que a la preparación se refiere, el `for` es relativamente peculiar,
-sirve para ejecutar el primer bloque para un contexto concreto, el creado por
-una sentencia de preparación. Si la preparación falla el bucle se rompe.
-
-El uso más común del `for` es el de iterar en secuencias gracias al operador
-`in` que mencionamos anteriormente pero que en este caso toma un uso distinto:
-
-``` python
->>> for i in [0,1,2,3]:
-... i+2
-...
-2
-3
-4
-5
-```
-
-Como puedes ver, el bloque interno `i+2` se ejecuta en cuatro ocasiones,
-cambiando el valor de `i` a los valores internos de la lista `[0,1,2,3]`.
-Cuando la lista termina, la preparación falla porque no quedan elementos y el
-bucle se rompe.
-
-Este último ejemplo es una receta de amplio uso que te aconsejo memorizar.
-
-
-#### Truthy and Falsey
-
-En este tipo de sentencias donde se comprueba si una condición es verdadera o
-falsa, python automáticamente trata de convertir el resultado a Boolean usando
-la función `bool` que ya conoces del apartado sobre tipos. No es necesario que
-conviertas a Boolean manualmente ya que estas sentencias disponen de una
-conversión implícita a Boolean[^truthy].
-
-Por tanto, cualquier resultado que tras pasar por la función `bool` dé como
-resultado `True` será considerado verdadero y viceversa. A estos valores se les
-conoce habitualmente como *truthy* y *falsey* porque no son `True` o `False`
-pero se comportan como si lo fueran. Algunos ejemplos curiosos:
-
-``` python
->>> bool([])
-False
->>> bool(None)
-False
->>> bool("")
-False
->>> bool(" ")
-True
->>> bool([None])
-True
-```
-
-Es relativamente sencillo prever qué valores son *truthy* o *falsey*,
-normalmente los valores que representan un vacío se consideran `False`.
-
-[^truthy]: <https://docs.python.org/3/library/stdtypes.html#truth-value-testing>
-
-
-### List comprehensions
-
-Una de las excepciones sintácticas que sí que podemos explicar en este momento,
-en el que ya sabes hacer bucles, son las *list comprehensions*. Python dispone
-de un sistema para crear secuencias y transformarlas muy similar a la notación
-de construcción de sets de las matemáticas[^set-notation].
-
-Como mejor se entiende es con unos ejemplos, en este caso vamos usar la función
-`range` para crear una lista de números del `0` (inclusive) al `10` (no
-inclusive). Usando la ayuda puedes saber más sobre la función `range`.
-
-
-> TODO:
-> Mejorar este ejemplo, es demasiado simple
-
-``` python
->>> [i**2 for i in range(0, 10)]
-[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
->>> tuple(i**2 for i in range(0, 10))
-(0, 1, 4, 9, 16, 25, 36, 49, 64, 81)
->>> { str(i): i**2 for i in range(0, 10)}
-{'0': 0, '1': 1, '2': 4, '3': 9, '4': 16, '5': 25, '6': 36, '7': 49, '8': 64,
-'9': 81}
-
->>> [i**2 for i in range(0, 10) if i > 5 ]
-[36, 49, 64, 81]
-```
-
-Como ves, en el caso de los diccionarios es necesario crear las claves también.
-En este caso las creamos convirtiendo el propio número a string con la función
-`str`.
-
-En los primeros ejemplos, de una secuencia de números hemos creado una
-secuencia de números al cuadrado. Pero las *list comprehensions* son más
-poderosas que eso, pudiendo a llegar a complicarse sobremanera añadiendo
-condiciones, como en el último de los ejemplos, para filtrar algunos casos.
-
-Te habrá mosqueado el uso de `tuple` para crear la tupla. Todo tiene una razón.
-La tupla, al estar formada por paréntesis, python no tiene claro si son
-paréntesis de precedencia o de creación de tupla, y considera que son los
-primeros, dando como resultado un generador, un paso intermedio de nuestro
-proceso que dejamos para el futuro.
-
-``` python
->>> (i**2 for i in range(0, 10))
-<generator object <genexpr> at 0x7f779d9b2d58>
-```
-
-[^set-notation]: <https://en.wikipedia.org/wiki/Set-builder_notation>
-
-### Excepciones
-
-Las excepciones o *exception* son errores del programa, python lanza
-excepciones cuando hay problemas. Por ejemplo, cuando intentas acceder a un
-índice inexistente en una lista.
-
-Las excepciones terminan la ejecución del programa a no ser que se gestionen.
-Se consideran fallos de los que el programa no puede arreglarse a no ser que se
-le indique cómo. Algunas funciones y librerías lanzan excepciones que nosotros
-debemos gestionar, por ejemplo: que un archivo no exista, o que no se tenga
-permisos de edición en el directorio, etc. Es nuestra responsabilidad como
-programadores tener un plan be o aceptar la excepción deportivamente a
-sabiendas que nuestro programa terminará indicando un error.
-
-Hay ocasiones en las que las excepciones pueden capturarse y otras no, por
-ejemplo, los fallos de sintaxis no pueden solventarse.
-
-Las excepciones se capturan con un `try-except` que, si programas en otros
-lenguajes como Java, probablemente conozcas como `try-catch`.
-
-```python
-try:
- # Bloque donde pueden ocurrir excepciones.
-except tipo1:
- # Bloque a ejecutar en caso de que se dé una excepción de tipo1.
- # Especificar el tipo también es opcional, si no se añade captura todos.
-except tipoN:
- # Bloques adicionales (opcionales) a ejecutar en caso de que se dé una
- # excepción de tipoN, que no haya sido capturada por los bloques
- # anteriores.
-finally:
- # Bloque (opcional) a ejecutar después de lo anterior, haya o no haya
- # habido excepción.
-```
-
-Además de capturarse, las excepciones pueden lanzarse con la sentencia `raise`.
-
-``` python
-if not input:
- raise ValueError("Invalid input")
-```
-
-Si el ejemplo anterior se diera dentro de una pieza de código que no podemos
-controlar, podríamos capturar el `ValueError` y evitar que la ejecución de
-nuestro programa terminara.
-
-``` python
-try:
- # Bloque que puede lanzar un ValueError
-except ValueError:
- print("Not value in input, using default")
- input = None
-```
-
-Aunque aún no hemos entrado en la programación orientada a objetos, te adelanto
-que las excepciones se controlan como tal. Hay excepciones que serán hijas de
-otras, por lo que usando la excepción genérica de la familia seremos capaces de
-capturarlas, o podremos crear nuevas excepciones como hijas de las que python
-ya dispone de serie. Por ahora recuerda que debes ordenar los bloques `except`
-de más concreto a más genérico, porque si lo haces al revés, los primeros
-bloques te capturarán todas las excepciones y los demás no tendrán ocasión de
-capturar ninguna, perdiendo así el detalle de los fallos.
-
-Cuando aprendas sobre programación orientada a objetos en el apartado
-correspondiente puedes volver a visitar este punto y leer la documentación de
-python[^exception] para entender cómo hacerlo. Te adelanto que python tiene una
-larga lista de excepciones y que está considerado una mala práctica crear
-nuevas si las excepciones por defecto cubren un caso similar al que se
-encuentra en nuestro programa.
-
-[^exception]: <https://docs.python.org/3/library/exceptions.html>
-
-### Funciones
-
-Las funciones sirven, sobre todo, para reutilizar código. Si una pieza de
-código se utiliza en más de una ocasión en tu programa, es una buena candidata
-para agruparse en una función y poder reutilizarla sin necesidad de duplicar
-el código fuente. Aunque sirven para más fines y tienen detalles que
-aclararemos en un capítulo propio, en este apartado adelantaremos cómo se
-definen y cómo lanzan y posteriormente las analizaremos en detalle.
-
-Las definición de funciones se realiza con una estructura similar a las
-anteriores, una línea descriptiva terminada en dos puntos (`:`) y después un
-bloque con mayor sangría para definir el cuerpo.
-
-``` python
-def nombre_de_funcion (argumentos):
- """
- docstring, un string multilínea que sirve como documentación
- de la función. Es opcional y no tiene efecto en el funcionamiento
- de la función.
- Es lo que se visualiza al ejecutar `help(nombre_de_función)`
- """
- # Cuerpo de la función
-```
-
-Para llamar a esta función que acabamos de crear:
-
-``` python
-nombre_de_función(argumentos)
-```
-
-El nombre de la función debe cumplir las mismas normas que los nombres de las
-referencias de las que ya hemos hablando anteriormente. Y esto debe ser así
-básicamente porque... ¡el nombre de la función también es una referencia a un
-valor de tipo función!
-
-Pronto ahondaremos más en este tema. De momento recuerda la declaración de las
-funciones. Dentro de ellas podrás incluir todas las estructuras definidas en
-este apartado, incluso podrás definir funciones.
-
-Aunque en el próximo capítulo tocará hablar de los argumentos de entrada, de
-momento te adelanto que cuando llamaste a la función `range` anteriormente, le
-introdujiste dos argumentos. Esos dos argumentos deben declararse como
-argumentos de entrada. Probablemente de una forma similar a esta:
-
-``` python
-def range (inicio, fin):
- contador = inicio
- salida = []
- while contador < fin:
- salida.append(contador)
- contador = contador + 1
- return salida
-```
-
-Lo que va a ocurrir al llamar a la función, por ejemplo, con esta llamada:
-`range(1, 10)` es que el argumento `inicio` tomará el valor `1` para esta
-ejecución y el argumento `fin` tomará el valor `10`, como si en el propio
-cuerpo de la función alguien hubiese hecho:
-
-``` python
-inicio = 1
-fin = 10
-```
-
-El contenido de la función se ejecutará, por tanto, con esas referencias
-asignadas a un valor. Con lo que sabes ya puedes intentar descifrar el
-comportamiento de la función `range` que hemos definido, que es similar, pero
-no igual, a la que define python.
-
-Sólo necesitas entender lo que hace la función `list.append` que puedes
-comprobar en la ayuda haciendo `help( list.append )` y la sentencia `return`,
-que se explica en el siguiente apartado.
-
-Prueba a leer ambas y a crear un archivo de python donde construyes la función
-y le lanzas unas llamadas. A ver si lo entiendes.
-
-### Sentencias útiles
-
-Python dispone de un conjunto de sentencias que pueden facilitar y flexibilizar
-mucho el uso de las estructuras que acabamos de visitar.
-
-#### Pass
-
-`pass` es una sentencia vacía, que no ejecuta nada. Es necesaria debido a las
-normas de sangría de python. Si construyes un bloque y no quieres rellenarlo
-por la razón que sea, debes usar `pass` en su interior porque, si no lo haces y
-simplemente lo dejas vacío, la sintaxis será incorrecta y python lanzará una
-excepción grave diciéndote que esperaba un bloque con sangría y no se lo diste.
-
-``` python
->>> if True:
-...
- File "<stdin>", line 2
-
- ^
-IndentationError: expected an indented block
->>> if True:
-... pass
-...
-```
-
-Suele utilizarse cuando no quiere tratarse una excepción o cuando se ha hecho
-un boceto de una función que aún no quiere desarrollarse, para que el
-intérprete no falle de forma inevitable.
-
-> NOTA: Las excepciones de sintaxis son las más graves, implican que el
-> intérprete no es capaz de entender lo que le pedimos así que la ejecución del
-> programa no llega a realizarse. La sintaxis se comprueba en una etapa previa
-> a la ejecución.
-
-#### Continue
-
-`continue` sirve para terminar el bucle actual y volver a comprobar la
-condición para decidir si volver a ejecutarlo.
-
-En el siguiente ejemplo salta la ejecución para el caso en el que `i` es `2`.
-
-``` python
->>> for i in [0, 1, 2, 3]:
-... if i == 2:
-... continue
-... i
-...
-0
-1
-3
-```
-
-#### Break
-
-`break` rompe el bucle actual. A diferencia del `continue`, no se intenta
-ejecutar la siguiente sentencia, simplemente se rompe el bucle completo. En el
-siguiente ejemplo se rompe el bucle cuando `i` es `2` y no se recupera.
-
-``` python
->>> for i in [0, 1, 2, 3]:
-... if i == 2:
-... break
-... i
-...
-0
-1
-```
-
-El `break` se usa mucho para romper bucles que son infinitos por definición. En
-lugar de añadir una condición compleja a un bucle `while`, por ejemplo, puedes
-usar un `True` en su condición e introducir un `if` más simple dentro de éste
-con un `break` que termine el bucle en los casos que te interesen. O puedes
-capturar una excepción y romper el bucle si ocurre.
-
-Además, es muy usado en búsquedas y habilita el uso del `else` en las
-sentencias `for`. Te propongo como ejercicio que trates de ejecutar y
-comprender el funcionamiento de esta pieza de código de python avanzado antes
-de seguir leyendo:
-
-``` python
-text = "texto de prueba"
-pos = 0
-
-for i in text:
- if i == "b":
- print("Found b in position: " + str(pos))
- break
- pos = pos + 1
-else:
- print("b not found :( ")
-```
-
-Si cambias el texto de la variable `text` por uno que no tenga letra `b` verás
-que el bloque `else` se ejecuta. Esto se debe a que el `for` no termina de
-forma abrupta, sino que itera por el string completo.
-
-Si quieres, puedes memorizar esta estructura para cuando quieras hacer
-búsquedas. Es elegante y te será útil.
-
-
-#### Return
-
-La sentencia `return` sólo tiene sentido dentro de las funciones. Sirve para
-finalizar la ejecución de una función sustituyendo su llamada por el resultado
-indicado en el `return`. Esta operación rompe todos los bucles por completo.
-
-En el apartado de las funciones profundizaremos en el uso del `return` pero es
-importante mencionarlo aquí porque su funcionalidad puede sustituir al `break`
-en muchas ocasiones.
-
-## Lo que has aprendido
-
-Es difícil resumir todo lo que has aprendido en este capítulo porque, la verdad
-es que es mucha información. Pero no pasa nada porque este capítulo se ha
-creado más como referencia que como otra cosa. No tengas miedo a volver a
-leerlo todas las veces que necesites. A todos se nos olvida cómo hay que
-declarar las funciones si llevamos mucho tiempo sin tocar python, o si era
-`try-catch` o `try-except`. Este capítulo pretende, por un lado, darte una
-pincelada de cómo se escribe en python y, por otro, servir como manual de
-consulta posterior.
-
-Pero, por hacer la labor de resumen, has aprendido el orden de ejecución de las
-órdenes y como alterarlo con bucles y condicionales. Para ello, has tenido que
-aprender lo que es un *bloque* de código, una pieza fundamental para entender
-la sintaxis. Tras ver varios ejemplos de bucles y condicionales, te has
-sumergido en la verdad y la mentira mediante los valores *truthy* y *falsey*,
-para después avanzar a las *list comprehensions*, cuyo nombre contiene *list*
-pero valen para cualquier dato complejo.
-
-Has cambiado un poco de tema después, saltando a las excepciones, que no has
-podido ver en detalle por no ser, aún, un experto en programación orientada a
-objetos. Pero tranquilo, pronto lo serás.
-
-Una pincelada sobre funciones ha sido suficiente para que no les tengas miedo
-nunca más y que podamos atacar el siguiente capítulo con energía, ya que ahora
-toca jugar con funciones hasta entenderlas por completo.
-
-Las sentencias útiles que hemos recopilado al final permiten juguetear con
-todas las estructuras que hemos definido en el capítulo de modo que puedas
-usarlas a tu antojo de forma cómoda. Algunas de ellas como `continue` y `break`
-no son realmente necesarias, puede programarse evitándolas y, de hecho, en
-algunos lugares enseñan a no usarlas, como si de una buena práctica se tratara,
-cambiando las condiciones de los bucles para que hagan esta labor. En este
-documento se muestran porque, en primer lugar, si lees código escrito por otras
-personas las encontrarás y tendrás que entender qué hacen y, en segundo, porque
-son sentencias que simplifican el código haciéndolo más legible o más sencillo
-por muy impuras que a algunos programadores les puedan parecer.
diff --git a/src/04_funciones.md b/src/04_funciones.md
deleted file mode 100644
index 618ccd6..0000000
--- a/src/04_funciones.md
+++ /dev/null
@@ -1,654 +0,0 @@
-# Funciones
-
-El objetivo de este capítulo es que te familiarices con el uso de las
-funciones. Parece sencillo pero es una tarea un tanto complicada porque, visto
-como nos gusta hacer las cosas, tenemos una gran cantidad de complejidad que
-abordar.
-
-Antes de entrar, vamos a definir una función y a usarla un par de veces:
-
-``` python
-def inc(a):
- b = a + 1
- return b
-```
-
-Si la llamamos:
-
-``` python
->>> inc(1)
-2
->>> inc(10)
-11
-```
-
-Cuidado con las declaraciones internas en las funciones. Si preguntamos por
-`b`:
-
-``` python
->>> b
-Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
-NameError: name 'b' is not defined
-```
-
-Parece que no conoce el nombre `b`. Esto es un tema relacionado con el *scope*.
-
-## Scope
-
-Anteriormente se ha dicho que python es un lenguaje de programación con gestión
-automática de la memoria. Esto significa que él mismo es capaz de saber cuando
-necesita pedir más memoria al sistema operativo y cuando quiere liberarla.
-El *scope* es un resultado este sistema. Para que python pueda liberar
-memoria, necesita de un proceso conocido como *garbage collector* (recolector
-de basura), que se encarga de buscar cuando las referencias ya no van a poder
-usarse más para pedir una liberación de esa memoria. Por tanto, las referencias
-tienen un tiempo de vida, desde que se crean hasta que el recolector de basura
-las elimina. Ese tiempo de vida se conoce como *scope* y, más que en tiempo, se
-trata en términos de espacio en el programa.
-
-El recolector de basura tiene unas normas muy estrictas y conociéndolas es
-fácil saber en qué espacio se puede mover una referencia sin ser disuelta.
-
-Resumiendo mucho, las referencias que crees se mantienen vivas hasta que la
-función termine. Como en el caso de arriba la función en la que se había creado
-`b` había terminado, `b` había sido limpiada por el recolector de basura. `b`
-era una referencia *local*, asociada a la función `inc`.
-
-Puede haber referencias declaradas fuera de cualquier función, que se llaman
-*globales*. Éstas se mantienen accesibles desde cualquier punto del programa, y
-se mantienen vivas hasta que éste se cierre. Considera que el propio programa
-es una función gigante que engloba todo.
-
-Python define que cualquier declaración está disponible
-en bloques internos, pero no al revés. El siguiente ejemplo lo muestra:
-
-``` python
-c = 100
-def funcion():
- a = 1
- # Se conoce c aquí dentro
-# Aquí fuera no se conoce a
-```
-
-El *scope* es peculiar en algunos casos que veremos ahora, pero mientras tengas
-claro que se extiende hacia dentro y no hacia fuera, todo irá bien.
-
-## First-class citizens
-
-Antes de seguir jugando con el *scope*, necesitas saber que las funciones en
-python son lo que se conoce como *first-class citizens* (ciudadanos de primera
-clase). Esto significa que pueden hacer lo mismo que cualquier otro valor.
-
-Las funciones son un valor más del sistema, como puede ser un string, y su
-nombre no es más que una referencia a ellas.
-
-Por esto mismo, pueden ser enviadas como argumento de entrada a otras
-funciones, devueltas con sentencias `return` o incluso ser declaradas dentro de
-otras funciones.
-
-Por ejemplo:
-
-``` python
->>> def filtra_lista(list):
-... def mayor_que_4(a):
-... return a > 4
-... return list( filter(mayor_que_4, lista) )
-...
->>> filtra_lista( [1,2,3,4,5,6,7] )
-[5, 6, 7]
-```
-
-En este ejemplo, haciendo uso de la función `filter` (usa la ayuda para ver lo
-que hace), filtramos todos los elementos mayores que `4` de la lista. Pero para
-ello hemos creado una función que sirve para compararlos y se la hemos
-entregado a la función `filter`.
-
-Este ejemplo no tiene más interés que intentar enseñarte que puedes crear
-funciones como cualquier otro valor y asignarles un nombre, para después
-pasarlas como argumento de entrada a otra función.
-
-## Lambdas
-
-Las funciones *lambda*[^lambda] o funciones anónimas son una forma sencilla de
-declarar funciones simples sin tener que escribir tanto. La documentación
-oficial de python las define como funciones para vagos.
-
-La sintaxis de una función lambda te la enseño con un ejemplo:
-
-``` python
->>> lambda x,y: x + y
-<function <lambda> at 0x7f035b879950>
->>> (lambda x,y: x + y)(1,2)
-3
-```
-
-En el ejemplo primero se muestra la declaración de una función y después
-colocando los paréntesis de precedencia y después de llamada a función se
-construye una función a la izquierda y se ejecuta con los valores `1` y `2`.
-
-Es fácil de entender la sintaxis de la función lambda, básicamente es una
-función reducida de sólo una sentencia con un `return` implícito.
-
-El ejemplo de la función `filtra_lista` puede reducirse mucho usando una
-función lambda:
-
-``` python
->>> def filtra_lista( lista ):
-... return list( filter(lambda x: x > 4, lista) )
-...
->>> filtra_lista( [1,2,3,4,5,6,7] )
-[5, 6, 7]
-```
-
-No necesitábamos una función con nombre en este caso, porque sólo iba a
-utilizarse esta vez, así que resumimos y reducimos tecleos.
-
-De todos modos, podemos asignarlas a una referencia para poder repetir su uso:
-
-``` python
->>> f = lambda x: x + 1
->>> f(1)
-2
->>> f(10)
-11
->>> f
-<function <lambda> at 0x7f02184febf8>
-```
-
-Las funciones lambda se usan un montón como *closure*, un concepto donde el
-*scope* se trabaja más allá de lo que hemos visto. Sigamos visitando el
-*scope*, para entender sus usos más en detalle.
-
-[^lambda]: Toman su nombre del Lambda
- Calculus:
- <https://en.wikipedia.org/wiki/Deductive_lambda_calculus>
-
-## Scope avanzado
-
-Cada vez que se crea una función, python crea un nuevo contexto para ella.
-Puedes entender el concepto de contexto como una tabla donde se van guardando
-las referencias que se declaran en la función. Cuando la función termina, su
-contexto asociado se elimina, y el recolector de basura se encarga de liberar
-la memoria de sus variables, tal y como vimos anteriormente.
-
-Lo que ocurre es que estos contextos son jerárquicos, por lo que, al crear una
-función, el padre del contexto que se crea es el contexto de la función madre.
-Python utiliza esto como método para encontrar las referencias. Si una
-referencia no se encuentra en el contexto actual, python la buscará en el
-contexto padre y así sucesivamente hasta encontrarla o lanzar un error diciendo
-que no la conoce. Esto explica por qué las variables declaradas en la función
-madre pueden encontrarse y accederse y no al revés.
-
-Aunque hemos explicado el *scope* como un concepto asociado a las funciones, la
-realidad es que hay varias estructuras que crean nuevos contextos en python. El
-comportamiento sería el mismo del que se ha hablado anteriormente, las
-referencias que se creen en ellos no se verán en el *scope* de nivel superior,
-pero sí al revés. Los casos son los siguientes:
-
-- Los módulos. Ver capítulo correspondiente
-- Las clases. Ver capítulo de Programación Orientada a Objetos.
-- Las funciones, incluidas las funciones anónimas o lambda.
-- Las expresiones generadoras[^generator-expression], que normalmente se
- encuentran en las *list-comprehension* que ya se han tratado en el capítulo
- previo.
-
-[^generator-expression]: <https://www.python.org/dev/peps/pep-0289/>
-
-
-### Scope léxico, Closures
-
-Hemos dicho que las funciones pueden declararse dentro de funciones, pero no
-hemos hablado de qué ocurre con el *scope* cuando la función declarada se
-devuelve y tiene una vida más larga que la función en la que se declaró. El
-siguiente ejemplo te pone en contexto:
-
-``` python
-def create_incrementer_function(increment):
- def incrementer (val):
- # Recuerda que esta función puede ver el valor `increment` por
- # por haber nacido en un contexto superior.
- return val + increment
- return incrementer
-
-increment10 = create_incrementer_function(10)
-increment10(10) # Returns 20
-increment1 = create_incrementer_function(1)
-increment1(10) # Returns 11
-```
-En este ejemplo hemos creado una función que construye funciones que sirven
-para incrementar valores.
-
-Las funciones devueltas viven durante más tiempo que la función que las
-albergaba por lo que saber qué pasa con la variable `increment` es difícil a
-simple vista.
-
-Python no destruirá ninguna variable que todavía pueda ser accedida, si lo
-hiciera, las funciones devueltas no funcionarían porque no podrían incrementar
-el valor. Habrían olvidado con qué valor debían incrementarlo.
-
-Para que esto pueda funcionar, las funciones guardan el contexto del momento de
-su creación, así que la función `incrementer` recuerda la primera vez que fue
-construida en un contexto en el que `increment` valía `10` y la nueva
-`incrementer` creada en la segunda ejecución de `create_incrementer_function`
-recuerda que cuando se creó `increment` tomó el valor `1`. Ambas funciones son
-independientes, aunque se llamen de la misma forma en su concepción, no se
-pisaron la una a la otra, porque pertenecían a contextos distintos ya que la
-función que las creaba terminó y luego volvió a iniciarse.
-
-Este funcionamiento donde el comportamiento de las funciones depende del lugar
-donde se crearon y no del contexto donde se ejecutan se conoce como *scope
-léxico*.
-
-Las *closures* son una forma de implementar el *scope léxico* en un lenguaje
-cuyas funciones sean *first-class citizens*, como es el caso de python, y su
-funcionamiento se basa en la construcción de los contextos y su asociación a
-una función capaz de recordarlos aunque la función madre haya terminado.
-
-Python analiza cada función y revisa qué referencias del contexto superior
-deben mantenerse en la función. Si encuentra alguna, las asocia a la propia
-función creando así lo que se conoce como *closure*, una función que recuerda
-una parte del contexto. No todas las funciones necesitan del contexto previo
-así que sólo se crean *closures* en función de lo necesario.
-
-Puedes comprobar si una función es una *closure* analizando su campo
-`__closure__`. Si no está vacío (valor `None`), significará que la función es
-una *closure* como la que ves a continuación. Una *closure* que recuerda un
-*int* del contexto padre:
-
-``` python
->>> f.__closure__
-(<cell at 0x7f04b4ebfa68: int object at 0xa68ac0>,)
-```
-
-Lo que estás viendo lo entenderás mejor cuando llegues al apartado de
-programación orientada a objetos. Pero, para empezar, ves que contiene una
-tupla con una `cell` de tipo *integer*.
-
-A nivel práctico, las *closures* son útiles para muchas labores que iremos
-desgranando de forma accidental. Si tienes claro el concepto te darás cuenta
-dónde aparecen en los futuros ejemplos.
-
-### `global` y `nonlocal`
-
-Hemos hablado de qué sentencias crean nuevos contextos, pero no hemos hablado
-de qué pasa si esos nuevos contextos crean referencias cuyo nombre es idéntico
-al de las referencias que aparecen en contextos superiores.
-
-Partiendo de lo que se acaba de explicar, y antes de adentrarnos en ejemplos,
-si se crea una función (o cualquiera de las otras estructuras) python creará un
-contexto para ella. Una vez creado, al crear una variable en este nuevo
-contexto, python añadirá una nueva entrada en su tabla hija con el nombre de la
-variable. Al intentar consultarla, python encontrará que en su tabla hija
-existe la variable y tomará el valor con el que la declaramos. Cuando la
-función termine, la tabla de contexto asociada a la función será eliminada.
-Esto siempre es así, independientemente del nombre de referencia que hayamos
-seleccionado. Por tanto, si el nombre ya existía en alguno de los contextos
-padre, lo ocultaremos, haciendo que dentro de esta función se encuentre el
-nombre recién declarado y no se llegue a buscar más allá. Cuando la función
-termine, como el contexto asociado a ésta no está en la zona de búsqueda de la
-función madre, en la función madre el valor seguirá siendo el que era.
-
-Ilustrándolo en un ejemplo:
-
-``` python
->>> a = 1
->>> def f():
-... a = 2
-... print(a)
-...
->>> f()
-2
->>> a
-1
-```
-
-Aunque el nombre de la referencia declarada en el interior sea el mismo que el
-de una referencia externa su declaración no afecta, lógicamente, al exterior
-ya que ocurre en un contexto independiente.
-
-Para afectar a la referencia global, python dispone de la sentencia `global`.
-La sentencia `global` afecta al bloque de código actual, indicando que los
-identificadores listados deben interpretarse como globales. De esta manera, si
-se reasigna una referencia dentro de la función, no será el contexto propio el
-que se altere, sino el contexto global, el padre de todos los contextos.
-
-``` python
->>> a = 1
->>> def f():
-... global a
-... a = 2
-... print(a)
-...
->>> f()
-2
->>> a
-2
-```
-
-> NOTA: Te recomiendo, de todas formas, que nunca edites valores globales desde
-> el cuerpo de funciones. Es más elegante y comprensible si los efectos de las
-> funciones sólo se aprecian en los argumentos de entrada y salida.
-
-Para más detalles sobre limitaciones y excepciones, puedes buscar en la ayuda
-ejecutando `help("global")`.
-
-El caso de `nonlocal` es similar, sin embargo, está diseñado para trabajar en
-contextos anidados. Es decir, en lugar de saltar a acceder a una variable
-global, `nonlocal` la busca en cualquier contexto que no sea el actual.
-`nonlocal` comienza a buscar las referencias en el contexto padre y va saltando
-hacia arriba en la jerarquía en busca de la referencia. Para saber más:
-`help("nonlocal")`.
-
-La diferencia principal entre ambas es que `global` puede crear nuevas
-referencias, ya que se sabe a qué contexto debe afectar: al global. Sin
-embargo, `nonlocal` necesita que la referencia a la que se pretende acceder
-esté creada, ya que no es posible saber a qué contexto se pretende acceder.
-
-Las sentencias `global` y `nonlocal` son tramposas, ya que son capaces de
-alterar el comportamiento del *scope léxico* y convertirlo en *scope dinámico*
-en casos extraños. El *scope dinámico* es el caso opuesto al léxico, en el que
-las funciones acceden a valores definidos en el contexto donde se ejecutan, no
-donde se crean.
-
-## Argumentos de entrada y llamadas
-
-Los argumentos de entrada se definen en la declaración de la función y se ha
-dado por hecho que es evidente que se separan por comas (`,`) y que, a la hora
-de llamar a la función, deben introducirse en el orden en el que se han
-declarado. Por mucho que esto sea cierto, requiere de una explicación más
-profunda.
-
-### Callable
-
-En python las funciones son un tipo de *callable*, «cosa que puede ser llamada»
-en inglés. Esto significa, de algún modo que hay otras cosas que pueden ser
-llamadas que no sean funciones. Y así es.
-
-Para python cualquier valor que soporte la aplicación de los paréntesis se
-considera «llamable». En el apartado de programación orientada a objetos
-entenderás esto en detalle. De momento, piensa que, igual que pasa al acceder a
-los campos de una colección usando los corchetes, siempre que python se
-encuentre unos paréntesis después de un valor tratará de ejecutar el valor. Así
-que los paréntesis no son una acción que únicamente pueda aplicarse en nombres
-de función[^lambdas-ejemplo] y python no lanzará un fallo de sintaxis cuando
-los usemos fuera de lugar, si no que será un fallo de tiempo de ejecución al
-darse cuenta de lo que se intenta ejecutar no es ejecutable.
-
-``` python
->>> 1()
-Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
-TypeError: 'int' object is not callable
-```
-
-[^lambdas-ejemplo]: Aunque en realidad esto ya lo has visto en los ejemplos de
- las funciones lambda.
-
-#### Caso de estudio: Switch Case
-
-Si quieres ver un ejemplo avanzado de esto, te propongo la creación de la
-estructura *switch-case* [^switch-case], que puede encontrarse en otros
-lenguajes, pero que en lugar de usar una estructura basada en un *if* con
-múltiples *elif* uses un diccionario de funciones.
-
-Las funciones son valores, por lo que pueden ocupar un diccionario como
-cualquier otro valor. Construyendo un diccionario en cuyas claves se encuentran
-los casos del *switch-case* y en cuyos valores se encuentran sus funciones
-asociadas se puede crear una sentencia con el mismo comportamiento.
-
-En el siguiente ejemplo se plantea una aplicación por comandos. Captura el
-tecleo del usuario y ejecuta la función asociada al comando. Las funciones no
-están escritas, pero puedes completarlas y analizar su comportamiento. Las
-palabras que no entiendas puedes consultarlas en la ayuda.
-
-``` python
-def borrar(*args):
- pass
-def crear(*args):
- pass
-def renombrar(*args):
- pass
-
-casos = {
- "borrar": borrar,
- "crear": crear,
- "renombrar": renombrar
-}
-
-comando = input("introduce el comando> ")
-
-try:
- casos[comando]()
-except KeyError:
- print("comando desconocido")
-
-```
-
-[^switch-case]: <https://en.wikipedia.org/wiki/Switch_statement>
-
-### Positional vs Keyword Arguments
-
-Las funciones tienen dos tipos de argumentos de entrada, aunque sólo hayamos
-mostrado uno de ellos de momento.
-
-El que ya conoces se denomina *positional argument* y se refiere a que son
-argumentos que se definen en función de su posición. Los argumentos
-posicionales deben ser situados siempre en el mismo orden, si no, los
-resultados de la función serán distintos. Las referencias `source` y `target`
-toman el primer argumento y el segundo respectivamente. Darles la vuelta
-resulta en el resultado opuesto al que se pretendía.
-
-``` python
-def move_file ( source, target ):
- "Mueve archivo de `source` a `target"
- pass
-
-move_file("file.txt", "/home/guido/doc.txt")
- # "file.txt" -> "/home/guido/doc.txt"
-move_file("/home/guido/doc.txt", "file.txt")
- # "/home/guido/doc.txt"-> "file.txt"
-```
-
-Los *keyword argument* o argumentos con nombre, por otro lado, se comportan
-como un diccionario. Su orden no importa pero es necesario marcarlos con su
-respectiva clave. Además, son opcionales porque en el momento de la declaración
-de la función python te obliga a que les asocies un valor por defecto
-(*default*). En el siguiente ejemplo se convierte la función a una basada en
-argumentos con nombre. No se han utilizado valores por defecto especiales, pero
-pueden usarse otros.
-
-``` python
-def move_file( source=None, target=None):
- "Mueve archivo de `source` a `target"
- pass
-
-move_file(source="file.txt", target="/home/guido/doc.txt")
- # "file.txt" -> "/home/guido/doc.txt"
-move_file(target="/home/guido/doc.txt", source="file.txt")
- # "file.txt" -> "/home/guido/doc.txt"
-```
-
-> NOTA: Si quieres que sean obligatorios, siempre puedes lanzar una excepción.
-
-Para funciones que acepten ambos tipos de argumento, es obligatorio declarar e
-introducir todos los argumentos posicionales primero. Es lógico, porque son
-los que requieren de una posición.
-
-También es posible declarar funciones que acepten cualquier cantidad de
-argumentos de un tipo u otro. Ésta es la sintaxis:
-
-``` python
-def argument_catcher( *args, **kwargs )
- "Función ejecutable con cualquier número de argumentos de entrada, tanto
- posicionales como con nombre."
- print( args )
- print( kwargs )
-```
-
-Los nombres `args` y `kwargs` son convenciones que casi todos los programadores
-de python utilizan, pero puedes seleccionar los que quieras. Lo importante es
-usar `*` para los argumentos posicionales y `**` para los argumentos con
-nombre.
-
-Prueba a ejecutar la función del ejemplo, verás que los argumentos posicionales
-se capturan en una tupla y los argumentos con nombre en un diccionario.
-
-Este tipo de funciones multiargumento se utilizan mucho en los *decorators*,
-caso que estudiaremos al final de este capítulo.
-
-#### Peligro: Mutable Defaults
-
-Existe un caso en el que tienes que tener mucho cuidado. Los valores por
-defecto en los argumentos con nombre se memorizan de una ejecución de la
-función a otra. En caso de que sean valores inmutables no tendrás problemas,
-porque su valor nunca cambiará, pero si almacenas en ellos valores mutables y
-los modificas, la próxima vez que ejecutes la función los valores por defecto
-habrán cambiado.
-
-La razón por la que los valores por defecto se recuerdan es que esos valores se
-construyen en la creación de la función, no en su llamada. Lógicamente, puesto
-que es en la sentencia `def` donde aparecen.
-
-``` python
->>> def warning(default=[]):
-... default.append(1)
-... return default
-...
->>> warning()
-[1]
->>> warning()
-[1, 1]
->>> warning()
-[1, 1, 1]
->>> warning()
-[1, 1, 1, 1]
-```
-
-## Decorators
-
-Los *decorators* son un concepto que, a pesar de ser bastante concreto, nos
-permite descubrir todo el potencial de lo que se acaba de tratar en este
-apartado. Sirven para dotar a las funciones de características adicionales.
-
-Por ejemplo, éste es un decorador que permite crear funciones que se ejecutan
-en un *thread* independiente. Tiene sentido para realizar acciones de las que
-se quiere que se ejecuten por su cuenta sin ralentizar el hilo principal del
-programa, como el envío de un email desde un servidor web.
-
-``` python
-import threading
-
-def run_in_thread(fn):
- def run(*args, **kwargs):
- t = threading.Thread(target=fn, args=args, kwargs=kwargs)
- t.start()
- return t
- return run
-
-@run_in_thread
-def send_mail():
- """
- Envía un email a un usuario, sin esperar confirmación.
- """
- pass
-```
-
-Hay muchos detalles que te habrán llamado la atención del ejemplo, el uso de
-`@run_in_thread` probablemente sea uno de ellos. Éste es, sin embargo, el
-detalle menos importante ya que únicamente se trata de un poco de *syntactic
-sugar*.
-
-> NOTA: el *syntactic sugar* son simplificaciones sintácticas que el lenguaje
-> define para acortar expresiones muy utilizadas. El ejemplo clásico de
-> *syntactic sugar* es:
-> `a += b`
-> Que es equivalente a:
-> `a = a + b`
-
-Los *decorators* pueden entenderse como un envoltorio para una función. No son
-más que una función que devuelve otra. En el caso del decorador del ejemplo,
-el *decorator* `run_in_thread` es función que recibe otra función como
-argumento de entrada y devuelve la función `run`. Este decorador, al aplicarlo
-a una función con `@run_in_thread` está haciendo lo siguiente:
-
-``` python
-send_mail = run_in_thread(send_mail)
-```
-
-> NOTA: `@decorator` es *syntactic sugar* de `fn = decorator(fn)`. Simplemente,
-> es más corto y más bonito.
-
-Por lo que la función `send_mail` ya no es lo que creíamos, sino la función
-`run`. En el ejemplo, la función `run` llama a la función `fn` de la función
-madre (`run` es una *closure*), que resulta ser `send_mail`, a modo de thread
-independiente.
-
-Como puedes apreciar, el hecho de capturar todos los posibles argumentos de
-entrada en la función `run` permite a `run_in_thread` decorar cualquier
-función, sabiendo que funcionará.
-
-El principal problema que los decoradores generan es que la función que hemos
-decorado ya no es la que parecía ser, así que su *docstring*, sus argumentos de
-entrada, etc. ya no pueden comprobarse desde la REPL usando la ayuda, ya que la
-ayuda buscaría la ayuda de la función devuelta por el decorador (`run` en el
-ejemplo). Usando `@functools.wraps`[^functools] podemos resolver este
-problema.
-
-[^functools]: Puedes leer por qué y cómo en la documentación oficial de
- python:
- <https://docs.python.org/3/library/functools.html#functools.wraps>
-
-La realidad es que los *decorators* son una forma muy elegante de añadir
-funcionalidades a las funciones sin complicar demasiado el código. Permiten
-añadir capacidad de depuración, *profiling* y todo tipo de funcionalidades que
-se te ocurran.
-
-Este apartado se deja varias cosas en el tintero, como los decoradores con
-parámetros de entrada, pero no pretende ser una referencia de cómo se usan,
-sino una introducción a un concepto útil que resume perfectamente lo tratado
-durante todo el capítulo.
-
-Te animo, como ejercicio, a que analices el decorador `@lru_cache` del módulo
-`functools` y comprendas su interés y su funcionamiento. Para leerlo en la
-ayuda debes importar el módulo `functools` primero. Como aún no sabes hacerlo,
-aquí tienes la receta:
-
-``` python
->>> import functools
->>> help(functools.lru_cache)
-```
-
-
-## Lo que has aprendido
-
-Este capítulo puede que sea el más complejo de todos los que te has encontrado
-y te encontrarás. En él has aprendido a declarar y a usar funciones, cosa
-sencilla, y todos los conceptos importantes relacionados con ellas. Un
-conocimiento que es útil en python, pero que puede ser extendido a casi
-cualquier lenguaje.
-
-Tras un sencillo acercamiento al *scope*, has comprendido que las funciones en
-python son sólo un valor más, como puede ser un `int`, y que pueden declararse
-en cualquier lugar, lo que te abre la puerta a querer declarar funciones
-sencillas sin nombre, que se conocen como funciones *lambda*.
-
-Una vez has aclarado que las funciones son ciudadanos de primera clase
-(*first-class citizens*) ya estabas preparado para afrontar la realidad del
-*scope* donde has tratado los contextos y cómo funcionan definiendo el concepto
-del *scope léxico* que, colateralmente, te ha enseñado lo que es una *closure*,
-un método para implementarlo. Pero también has tenido ocasión de aprender que
-en python es posible crear casos de *scope dinámico* mediante las sentencias
-`global` y `nonlocal`, que pueden ser útiles, pero es mejor no abusar de ellas.
-
-Pero no había quedado claro en su momento cómo funcionaban los argumentos de
-entrada y las llamadas a las funciones, así que has tenido ocasión de ver por
-primera vez lo que es un *callable* en python, aunque se te ha prometido
-analizarlo en el futuro. Lo que sí que has tenido ocasión de tratar son los
-argumentos *positional* y *keyword*, y cómo se utilizan en todas sus posibles
-formas.
-
-Finalmente, para agrupar todo esto en un único concepto, se te han mostrado los
-*decorators*, aunque de forma muy general, con el fin de que vieras que todo lo
-que se ha tratado en este capítulo aparece en conceptos avanzados y es
-necesario entenderlo si quieren llegar a usarse de forma eficiente.
diff --git a/src/05_oop.md b/src/05_oop.md
deleted file mode 100644
index 6fdb9ce..0000000
--- a/src/05_oop.md
+++ /dev/null
@@ -1,1093 +0,0 @@
-# Programación Orientada a Objetos
-
-La *programación orientada a objetos* u *object oriented programming* (OOP) es
-un paradigma de programación que envuelve python de pies a cabeza. A pesar de
-que python se define como un lenguaje de programación multiparadigma, la
-programación orientada a objetos es el paradigma principal de éste. A pesar de
-que varias de las características que tratamos en el apartado anterior se
-corresponden más con un lenguaje de programación funcional, en python **todo**
-(o casi todo) es una clase.
-
-Python usa una programación orientada a objetos basada en clases[^class], a
-diferencia de otros lenguajes como JavaScript, donde la orientación a objetos
-está basada en prototipos[^proto]. No es el objetivo de este documento el de
-contarte cuales son las diferencias entre ambas, pero es interesante que sepas
-de su existencia, ya que es una de las pocas diferencias que existen entre
-estos dos lenguajes de amplio uso en la actualidad.
-
-[^class]: <https://en.wikipedia.org/wiki/Class-based_programming>
-[^proto]: <https://en.wikipedia.org/wiki/Prototype-based_programming>
-
-## Programación basada en clases
-
-Tras haber hecho una afirmación tan categórica como que en python todo son
-clases, es nuestra obligación entrar a definir lo que son y qué implica la
-programación basada en clases.
-
-Los objetos, *objects*, son entidades que encapsulan un estado, un
-comportamiento y una identidad capaz de separarlos de otras entidades. Una
-clase, *class*, es la definición de estos objetos.
-
-Saliendo de la definición filosófica y trayéndola a un nivel de andar por casa,
-puedes aclararte sabiendo que las clases son la definición enciclopédica de
-algo, mientras que los objetos son el propio objeto, persona o animal descrito.
-
-Llevándolo al ejemplo de un perro, la clase es la definición de qué es un perro
-y los objetos son los distintos perros que te puedes encontrar en el mundo. La
-definición de perro indica qué características ha de tener un ente para ser un
-perro, como ser un animal, concretamente doméstico, qué anatomía debe tener,
-cómo debe comportarse, etc. Mientras que el propio perro es uno de los casos de
-esa definición.
-
-Cada perro tiene una **identidad propia** y es independiente de los otros,
-tiene un **comportamiento** concreto (corre, salta, ladra...) y tiene un
-**estado** (está despierto o dormido, tiene una edad determinada...).
-
-La diferencia entre una clase y un objeto tiene lógica si lo piensas desde la
-perspectiva de que python no tiene ni idea de lo que es un perro y tú tienes
-que explicárselo. Una vez lo haces, declarando tu clase, puedes crear
-diferentes perros y ponerlos a jugar. Lo bonito de programar es que tu programa
-es tu mundo y tú decides lo que es para ti (o para tu programa) un perro.
-
-A nivel práctico, los objetos son grupos de datos (el *estado*) y funciones (la
-*funcionalidad*). Estas funciones son capaces de alterar los datos del propio
-objeto y no de otro (se intuye el concepto de *identidad*). Analizándolo desde
-el conocimiento que ya tienes, es lógico pensar que un objeto es, por tanto,
-una combinación de valores y funciones accesible a modo de elemento único.
-Exactamente de eso se trata.
-
-Existe una terminología técnica, eso sí, para referirse a esos valores y a esas
-funciones. Normalmente los valores se conocen como *propiedades* del objeto y
-las funciones se conocen como *métodos*. Así que siempre que hagamos referencia
-a cualquiera de estas dos palabras clave debes recordar que hacen referencia a
-la programación orientada a objetos.
-
-### Fundamento teórico
-
-La programación basada en clases se basa en tres conceptos fundamentales que
-repasaremos aquí de forma rápida para razonar el interés de la programación
-orientada a objetos sobre otros paradigmas.
-
-La **encapsulación**[^encapsulation] trata de crear datos con sus métodos
-propios para alterarlos de modo que restrinjan el acceso directo al contenido
-de estos datos con el fin de asegurar una coherencia o robustez interna. Puedes
-entender esto como una forma de esconder información o como mi profesor de
-programación II en la universidad solía decir: «Las patatas se pelan en la
-cocina del restaurante, no en el comedor». La utilidad de la encapsulación es
-la de aislar secciones del programa para tener total control sobre su
-contenido gracias a tener total control de la vía de acceso a estos datos. A
-nivel práctico este concepto puede usarse para, por ejemplo, obligar a que un
-objeto sólo pueda ser alterado en incrementos controlados en lugar de poder
-pisarse con un valor arbitrario.
-
-La **herencia**[^inheritance] es un truco para reutilizar código de forma
-agresiva que, casualmente, sirve como una buena forma de razonar. Aporta la
-posibilidad de crear nuevas *clases* a partir de clases ya existentes.
-Volviendo a la simplificación anterior, si una clase es una definición
-enciclopédica de un concepto, como un perro, puede estar basada en otra
-descripción para evitar contar todo lo relacionado con ella. En el caso del
-perro, el perro es un animal. Animal podría ser otra clase definida previamente
-de la que el perro heredara y recibiera gran parte de su descripción genérica
-para sólo cubrir puntos que necesite especificar como el tamaño, la forma, el
-tipo de animal, el comportamiento concreto, etc. Existe la posibilidad de hacer
-herencias múltiples también ya que algunos conceptos pueden describirse en dos
-superclases distintas: un perro es un animal (vive, muere, se alimenta, se
-reproduce) y también es terrestre (camina sobre una superficie, etc). Ambos
-conceptos son independientes: los coches también son terrestres pero no son
-animales y los peces también son animales pero no terrestres.
-
-Y, finalmente, el **polimorfismo**[^polymorphism]. La propia etimología de la
-palabra define con bastante precisión el concepto, pero aplicarlo a la
-programación orientada a objetos no es tan evidente. Existen varios tipos de
-polimorfismo pero el más sencillo es entender el *subtyping*[^subtyping]. Una
-vez lo comprendas el resto será evidente. Si volvemos al ejemplo del perro,
-para ciertos comportamientos, nos da igual que tratemos de perros, de peces o
-de pájaros, todos son animales y todos los animales se comportan de la misma
-forma. Es decir, todas las subclases señaladas comparten el comportamiento de
-la superclase animal. Si esto es cierto, puede suponerse que en cualquier caso
-en el que se espere un objeto de la clase animal es seguro usar una subclase de
-ésta.
-
-Visto desde otra perspectiva, las subclases comparten comportamiento porque
-reutilizan las funciones de la clase principal o las redefinen (*herencia*),
-pero podemos asegurar que todas las subclases tienen un conjunto de funciones
-con la misma estructura, independientemente de lo que hagan, que aseguran que
-siempre van a ser compatibles. El nombre de esta cualidad viene a que un perro
-puede tomar la forma de un animal.
-
-Los otros tipos de polimorfismo explotan el mismo comportamiento de diferentes
-maneras, mientras que recuerdes que es posible programar de modo que el tipo de
-los datos que trates sea indiferente o pueda variar es suficiente. Otro ejemplo
-de esto son los operadores matemáticos, que son capaces de funcionar en
-cualquier tipo de número (integer, float, complex, etc.) de la misma manera, ya
-que todos son números, al fin y al cabo.
-
-Entender estos conceptos a nivel intuitivo, sin necesidad de entrar en los
-detalles específicos de cada uno, es interesante para cualquier programador y
-facilita de forma radical la comprensión de muchas de las decisiones de diseño
-tomadas en python y en proyectos relacionados aunque también, por supuesto, de
-otros lenguajes y herramientas.
-
-[^encapsulation]: https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)
-[^inheritance]: https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)
-[^polymorphism]: https://en.wikipedia.org/wiki/Polymorphism_(computer_science)
-[^subtyping]: https://en.wikipedia.org/wiki/Subtyping
-
-
-## Sintaxis
-
-En el siguiente ejemplo se muestra la sintaxis básica a la hora de crear una
-clase y después instanciar dos nuevos objetos `bobby` y `beltza`. Los puntos
-(`.`) se utilizan para indicar a quién pertenece el método o propiedad al que
-se hace referencia (*identidad*). De este modo, no ocurrirá lo mismo cuando el
-perro (`Dog`) `bobby` ladre (`bark`) que cuando lo haga el perro `beltza`.
-
-Los métodos describen la *funcionalidad* asociada a los perros en general, pero
-además, la función `bark` los describe en particular, haciendo que cada perro
-tome su nombre (`name`), una propiedad o dicho de otro modo, su *estado*.
-
-``` python
-class Dog:
- type = "canine"
- def __init__(self, name):
- self.name = name
- def bark(self):
- print("Woof! My name is " + self.name)
-
-bobby = Dog("Bobby") # New Dog called Bobby
-beltza = Dog("Beltza") # New Dog called Beltza
-
-bobby.name # Bobby
-beltza.name # Beltza
-
-bobby.type # canine
-beltza.type # canine
-
-bobby.bark() # Prints "Woof! My name is Bobby"
-beltza.bark() # Prints "Woof! My name is Beltza"
-```
-
-### Creación de objetos
-
-El ejemplo muestra cómo crear nuevos *objetos* de la clase `Dog`. Las llamadas
-a `Dog("Bobby")` y `Dog("Beltza")` crean las diferentes instancias de la clase.
-
-Llamar a los nombres de clase como si de funciones se tratara crea una
-instancia de éstas. Los argumentos de entrada de la llamada se envían como
-argumentos de la función `__init__` declarada también en el propio ejemplo.
-Entiende de momento que los argumentos posicionales se introducen a partir de
-la segunda posición, dejando el argumento llamado `self` en el ejemplo para un
-concepto que más adelante entenderás.
-
-En el ejemplo, por tanto, se introduce el nombre (`name`) de cada `Dog` en su
-creación y la función `__init__` se encarga de asignárselo a la instancia
-recién creada mediante una metodología que se explica más adelante en este
-mismo capítulo. De momento no es necesario comentar en más profundidad estos
-detalles, con lo que sabes es suficiente para entender el funcionamiento
-general.
-
-Queda por aclarar, sin embargo, qué es la función `__init__` y por qué tiene un
-nombre tan extraño y qué es `type = canine`, que lo trataremos en próximos
-apartados de este capítulo.
-
-### Herencia
-
-Antes de entrar en los detalles propuestos en el apartado anterior, que tratan
-conceptos algo más avanzados, es interesante ver cómo definir clases mediante
-la herencia. Basta con introducir una lista de clases de las que heredar en la
-definición de la clase, entre paréntesis, como si de argumentos de entrada de
-una función se tratara, tal y como se muestra en la clase `Dog` del siguiente
-ejemplo ejecutado en la REPL:
-
-``` python
->>> class Animal:
-... def live(self):
-... print("I'm living")
-...
->>> class Terrestrial:
-... def move(self):
-... print("I'm moving on the surface")
-...
->>> class Dog(Animal, Terrestrial):
-... def bark(self):
-... print("woof!")
-... def move(self):
-... print("I'm walking on the surface")
-...
->>> bobby = Dog()
->>> bobby.bark()
-woof!
->>> bobby.live()
-"I'm living"
->>> bobby.move()
-"I'm walking on the surface"
-```
-
-El ejemplo muestra un claro uso de la herencia. La clase `Dog` hereda
-automáticamente las funciones asociadas a las superclases, pero es capaz de
-definir las propias e incluso redefinir algunas. Independientemente de la
-redefinición del método `move`, cualquier perro (`Dog`) va a ser capaz de
-moverse por la superficie, porque la superclase `Terrestrial` ya le da los
-métodos necesarios para hacerlo. Lo que ocurre es que cualquier subclase de
-`Terrestrial` tiene la ocasión moverse (`move`) a su manera: en el caso del
-perro, caminando.
-
-> NOTA: La herencia es interesante, pero tampoco debe caerse en la psicosis de
-> añadir demasiadas superclases. En ocasiones las superclases son necesarias,
-> sobre todo cuando aprovechar el polimorfismo facilita el trabajo, pero
-> usarlas de forma agresiva genera código extremadamente complejo sin razón.
-
-
-### Métodos de objeto o funciones de clase: `self`
-
-Los métodos reciben un parámetro de entrada llamado `self` que no se utiliza a
-la hora de llamarlos: al hacer `bobby.bark()` no se introduce ningún argumento
-de entrada a la función `bark`.
-
-Sin embargo, si no se añade el argumento de entrada a la definición del método
-`bark` y se llama a `bobby.bark()` pasa lo siguiente:
-
-``` python
->>> class Dog:
-... def bark():
-... pass
-...
->>> bobby = Dog()
->>> bobby.bark()
-Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
-TypeError: bark() takes 0 positional arguments but 1 was given
-```
-
-Python dice que `bark` espera `0` argumentos posicionales pero se le ha
-entregado `1`, que nosotros no hemos metido en la llamada, claro está. Así que
-ha debido de ser él.
-
-Efectivamente, python introduce un argumento de entrada en los métodos, el
-argumento de entrada que por convención se suele llamar `self`. Este parámetro
-es el propio `bobby` en este caso.
-
-> NOTA: Por convención se le denomina `self`. Tú le puedes llamar como te
-> apetezca pero, si pretendes que otros programadores te entiendan, mejor
-> `self`.
-
-Para explicar por qué ocurre esto es necesario diferenciar bien entre clase y
-objeto. Tal y como hemos hecho antes con las definiciones enciclopédicas
-(*clase*) y los conceptos del mundo real que encajan en la definición
-(*objeto*). Los objetos también se conocen como instancias, son piezas de
-información independiente que han sido creadas a partir de la definición que la
-clase aportaba.
-
-En python las clases tienen la posibilidad de tener funciones, que definen el
-comportamiento de la clase y no el de los objetos que se crean desde ellas.
-Ten en cuenta que las clases también deben procesarse y ocupan un espacio en la
-memoria, igual que te ocurre a ti, puedes conocer un concepto y su
-comportamiento y luego muchos casos que cumplan ese concepto y ambas cosas
-son independientes. Esta posibilidad aporta mucha flexibilidad y permite
-definir clases complejas.
-
-Ahora bien, para python las funciones de clase y los métodos (de los objetos,
-si no no se llamarían métodos), se implementan de la misma manera. Para la
-clase ambas cosas son lo mismo. Sin embargo, el comportamiento del operador
-punto (`.`), que dice a quién pertenece la función o método, es diferente si el
-valor de la izquierda es una clase o un objeto. Introduciendo en el segundo
-caso el propio objeto como primer parámetro de entrada, el `self` del que
-hablamos, para que la clase sepa qué objeto tiene que alterar. Este es el
-mecanismo de la *identidad* del que antes hablamos y no llegamos a definir en
-detalle. Cada objeto es único, y a través del `self` se accede a él.
-
-Es un truco interesante para no almacenar las funciones en cada uno de los
-objetos como método. En lugar de eso, se mantienen en la definición de la clase
-y cuando se llama al método, se busca de qué clase es el objeto y se llama a la
-función de la clase con el objeto como argumento de entrada.
-
-Dicho de otra forma, `bobby.bark()` es equivalente a `Dog.bark( bobby )`.
-
-Ilustrado en un ejemplo más agresivo, puedes comprobar que en función de a
-través de qué elemento se acceda a la función `bark` python la interpreta de
-forma distinta. A veces como función (*function*) y otras veces como método
-(*method*), en función de si se accede desde la clase o desde el objeto:
-
-``` python
->>> class Dog:
-... def bark(self):
-... pass
-...
->>> type ( Dog.bark)
-<class 'function'>
->>> type ( bobby.bark )
-<class 'method'>
-```
-
-> NOTA: También te habrás fijado, y si no lo has hecho es momento de hacerlo,
-> que los nombres de las clases empiezan por mayúscula en los ejemplos (`Dog`)
-> mientras que los objetos comienzan en minúscula (`bobby`). Se trata de otra
-> convención ampliamente utilizada para saber diferenciar entre uno y otro de
-> forma sencilla. Es evidente cuál es la clase y el objeto con los nombres que
-> hemos tratado en los ejemplos, pero en otros casos puede no serlo y con este
-> sencillo truco facilitas la lectura de tu código. Hay muchas ocasiones en las
-> que esta convención se ignora, así que cuidado.
-> Prueba a hacer `type(int)` en la terminal.
-
-### Variables de clase
-
-En el primer ejemplo del capítulo hemos postergado la explicación de `type =
-canine` y ahora que ya manejas la mayor parte de la terminología y dominas la
-diferencia entre una clase y una instancia de ésta (un *objeto*) es momento de
-recogerla. A continuación se recupera la sección del ejemplo para facilitar la
-consulta, fíjate en la línea 2.
-
-``` {.python .numberLines}
-class Dog:
- type = "canine"
- def __init__(self, name):
- self.name = name
- def bark(self):
- print("Woof! My name is " + self.name)
-```
-
-`type` es lo que se conoce como una *variable de clase* (*class variable*).
-
-> NOTA: En este documento se ha evitado de forma premeditada usar la palabra
-> *variable* para referirse a los valores y sus referencias con la intención de
-> marcar la diferencia entre ambos conceptos. En este apartado, sin embargo, a
-> pesar de que se siga tratando de una referencia, se usa el nombre *class
-> variable* porque es como se le llama en la documentación[^class_var] y así
-> será más fácil que lo encuentres si en algún momento necesitas buscar
-> información al respecto. De esto ya hemos discutido en el capítulo sobre
-> datos, donde decimos que *todo es una referencia*.
-
-[^class_var]: <https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables>
-
-Previamente hemos hablado de que los objetos pueden tener propiedades
-asociadas, y cada objeto tendrá las suyas. Es decir, que cada instancia de la
-clase puede tener sus propias propiedades independientes. El caso que tratamos
-en este momento es el contrario, el `type` es un valor que comparten **todas**
-las instancias de `Dog`. Cualquier cambio en esos valores los verán todos los
-objetos de la clase, así que hay que ser cuidadoso.
-
-El acceso es idéntico al que ocurriría en un valor asociado al objeto, como en
-el caso `name` del ejemplo, pero en este caso observas que en su declaración en
-la clase no es necesario indicar `self`, ya no es necesario decir cuál es la
-instancia concreta a la que se le asigna el valor: se le asigna a todas.
-
-A parte de poder acceder a través de los objetos de la clase, es posible
-acceder directamente desde la clase a través de su nombre, como a la hora de
-acceder a las funciones de clase: `Dog.type` resultaría en `"canine"`.
-
-> NOTA: Si en algún caso python viera que un objeto tiene propiedades y
-> variables de clase definidas con el mismo nombre, cosa que no debería ocurrir
-> a menudo, tendrán preferencia las propiedades.
-
-### Encapsulación explícita
-
-Es posible que te encuentres en alguna ocasión con métodos o propiedades,
-*campos* en general, cuyo nombre comience por `_` o por `__`. Se trata de casos
-en los que esas propiedades o métodos quieren ocultarse del exterior.
-
-El uso de `_` al inicio del nombre de un campo es una convención que avisa de
-que este campo no debe accederse desde el exterior de la clase y su objetivo es
-usarlo desde el interior de ésta.
-
-Esta convención se llevó al extremo en algún momento y se decidió crear un caso
-en el que esta convención inicial tuviera cierta funcionalidad añadida para las
-dobles barras bajas (`__`) que impidiera un acceso accidental a esos campos
-conocido como *name mangling*.
-
-#### Campos privados: *name mangling*
-
-El *name mangling* es un truco que hace python para asegurarse de que no se
-entra por accidente a las secciones que empiezan por `__`. Añade
-`_nombredeclase` al inicio de los campos, transformando su nombre final y
-dificultando el acceso por accidente.
-
-Ese acceso accidental no sólo es para que el programador no acceda, ya que, si
-se esfuerza la suficiente, va a poder hacerlo de igual modo, si no para que el
-propio python no acceda al campo que no corresponde. El hecho de añadir el
-nombre de la clase al campo crea una brecha en la herencia, haciendo que los
-campos no se hereden de la forma esperada.
-
-En una subclase en la que los campos de la clase madre han sido marcados con
-`__`, la herencia hace que estos campos se hereden con el nombre cambiado que
-contiene el nombre de la superclase. De este modo, es difícil para la subclase
-pisar estos campos ya que tendría que definirlos manualmente con el nombre
-cambiado. Crear nuevos campos con `__` no funcionaría, ya que, al haber
-cambiado de clase, el nombre generado será distinto.
-
-Este mecanismo es un truco para crear *campos privados*, concepto bastante
-común en otros lenguajes como Java o C++, que en python es inexistente.
-
-El concepto de los *campos privados* es interesante en la programación
-orientada a objetos. Pensando en la *encapsulación*, es lógico que a veces las
-clases definan métodos o propiedades que sólo los objetos creados a partir de
-ellas conozcan y que los objetos creados de clases heredadas no. Este es el
-método que python tiene para aportar esta funcionalidad.
-
-Es interesante añadir, por otro lado, que python es un lenguaje de programación
-muy dinámico por lo que la propia definición de las clases, y muchas cosas más,
-puede alterarse una vez creadas. Esto significa que el hecho de ocultar campos
-no es más que un acuerdo tácito entre programadores porque, si quisieran,
-podrían definir todo de nuevo. Trucos como este sirven para que el programador
-sea consciente de que está haciendo cosas que se supone que no debería hacer.
-Cuando programes en python, tómate esto como pistas que te indican cómo se
-supone que deberías estar usando las clases.
-
-### Acceso a la superclase
-
-A pesar de la herencia, no siempre se desea eliminar por completo la
-funcionalidad de un método o pisar una propiedad. A veces es interesante
-simplemente añadir funcionalidad sobre un método o recordar algún valor
-definido en la superclase.
-
-Python soporta la posibilidad de llamar a la superclase mediante la función
-`super`, que permite el acceso a cualquier campo definido en la superclase.
-
-``` python
-class Clase( SuperClase ):
- def metodo(self, arg):
- super().metodo(arg) # Llama a la definición de
- # `metodo` de `SuperClase`
-```
-
-> NOTA: `super` busca la clase previa por preferencia, si usas herencias
-> múltiples y pisas los campos puede complicarse.
-
-
-## Interfaces estándar: Duck Typing
-
-Una de las razones principales para usar programación orientada a objetos es
-que, si se eligen los métodos con precisión, pueden crearse estructuras de
-datos que se comporten de similar forma pero que tengan cualidades diferentes.
-Independientemente de cómo estén definidas sus clases, si dos objetos disponen
-de los mismos métodos podrán ser sustituidos el uno por el otro en el programa
-y seguirá funcionando aunque su funcionalidad cambie.
-
-Dicho de otra forma, dos objetos (o dos cosas, en general) podrán ser
-intercambiados si disponen de la misma *interfaz*. *Interfaz*, de *inter*:
-entre; y *faz*: cara, viene a significar algo así como «superficie de contacto»
-y es la palabra que se usa principalmente para definir la frontera compartida
-entre dos componentes o, centrándonos en el caso que nos ocupa, su conexión
-funcional.
-
-Si recuerdas la *herencia* y la combinas con estos conceptos, puedes
-interpretar que además de una metodología para reutilizar código es una forma
-de crear nuevas definiciones que soporten la misma interfaz.
-
-En otros lenguajes de programación, Java, por ejemplo, existe el concepto
-*interfaz* que serían una especie pequeñas clases que definen qué funciones
-debe cumplir una clase para que cumpla la interfaz. A la hora de crear las
-clases se les puede indicar qué interfaces implementan y el lenguaje se encarga
-de asegurarse de que el programador ha hecho todo como debe.
-
-El dinamismo de python hace que esto sea mucho más flexible. Debido a que
-python no hace casi comprobaciones antes de ejecutarse, necesita un método para
-mucho más directo. Para python, *si anda como un pato, vuela como un pato y
-nada como un pato: es un pato*.
-
-Python usa lo que en la terminología del lenguaje se conoce como
-*protocolos*[^protocol] (*protocol*) para que los objetos creados por el
-programador puedan comportarse como los que el propio sistema aporta. Por
-ejemplo, que sea posible utilizarlos como iterable en un `for`, que el sistema
-pueda cerrarlos de forma automática, buscar en ellos usando el operador `in`,
-etc. Simplemente, el sistema define qué funciones se deben cumplir en cada uno
-de esos casos y cuando se encuentre con ellos intentará llamarlas
-automáticamente. Si el elemento no dispone de esas funciones lanzará una
-excepción como la que lanza cuando intentamos acceder a un método que no existe
-(que es básicamente lo que estamos haciendo en este caso).
-
-> TODO: En realidad no se llaman protocolos todos ellos. Se llama así sólo al
-> *iterator protocol*. En realidad se llaman: [Special Method
-> Names](https://docs.python.org/3/reference/datamodel.html#special-method-names)
-
-En general, python, con el fin de diferenciar claramente qué nombres elige el
-programador y cuales han sido seleccionados por el lenguaje, suele utilizar una
-convención para la nomenclatura: comienzan y terminan por: `__`
-
-A continuación se describen algunos de los protocolos más comunes, algunos ya
-han aparecido a lo largo de los ejemplos del documento, otros las verás por
-primera vez ahora. Existen muchos más, y todos están extremadamente bien
-documentados. Si en algún momento necesitas crear algunos nuevos, la
-documentación de python es una buena fuente donde empezar.
-
-Todos las protocolos se presentan con un nombre, en muchos casos inventado,
-terminado en *-able*. Python utiliza también este tipo de nombres, como el ya
-aparecido *llamable*, o *callable* en inglés, que se refiere a cualquier cosa
-que puede ser llamada. Representar los nombres de esta manera sirve para
-expresar el interés de los protocolos. Si en algún momento necesitas crear una
-clase que defina un objeto en el que se puede buscar necesitas que sea un
-*buscable*, es decir, que soporte el protocolo que define ese comportamiento.
-
-[^protocol]: **Protocolo**: 5. m. Inform. Conjunto de reglas que se establecen
- en el proceso de comunicación entre dos sistemas. — RAE [Consultado
- 01-12-2019]: <https://dle.rae.es/protocolo>
-
-### *Representable*: `__repr__`
-
-Este protocolo sirve para otorgar a python una forma de representar estos
-objetos. Al ejecutar la función `print` o al exponer valores en la REPL
-(recuerda que la P significa print), python trata de visualizarlos.
-
-La el método `__repr__` se ejecuta justo antes de imprimirse el objeto, de
-forma automática. La función requiere que se devuelva un elemento de tipo
-string, que será el que después se visualice.
-
-En el ejemplo a continuación se comienza con la clase `Dog` vacía y se
-visualiza una de sus instancias. Posteriormente, se reasigna la función
-`__repr__` de `Dog` con una función que devuelve un string. Al volver a mostrar
-a `bobby` el resultado cambia.
-
-Como se ve en el ejemplo, es interesante tener una buena función de
-representación si lo que se pretende es entender el contenido de los objetos.
-
-> NOTA: Python ya aporta una forma estándar de representar los objetos, si la
-> función `__repr__` no se define simplemente se usará la forma estándar.
-
-``` python
->>> class Dog:
-... pass
-...
->>> bobby = Dog()
->>> bobby
-<__main__.Dog object at 0x7fb7fba1b908>
-
->>> Dog.__repr__ = lambda self: "Dog called: " + self.name
->>> bobby.name = "Bobby"
->>> bobby
-Dog called: Bobby
->>>
-```
-
-### *Contable*: `__len__`
-
-En python se utiliza la función `len` para comprobar la longitud de cualquier
-elemento contable. Por ejemplo:
-
-``` python
->>> len( (1,2,3) )
-3
-```
-
-Las objetos que soporten esta función podrán contarse para conocer su longitud
-mediante la función `len`. Python llamará al método `__len__` del objeto (que
-se espera que devuelva un número entero) y ésta será su longitud. Siguiendo con
-el ejemplo del protocolo anterior:
-
-``` python
->>> Dog.__len__ = lambda self: 12 # Siempre devuelve 12
->>> len(bobby)
-12
-```
-
-Este protocolo permite crear elementos contables, en lugar de los típicos
-diccionario, tupla y lista. Como por ejemplo los ya existentes `NamedTuple`,
-`OrderedDict` y otros. Los protocolos para el *buscable* e *iterable* también
-son muy interesantes para esta labor.
-
-### *Buscable*: `__contains__`
-
-El método `__contains__` debe devolver `True` o `False` y recibir un argumento
-de entrada. Con esto el objeto será capaz de comprobarse con sentencias que
-hagan uso del operador `in` (y `not in`). Las dos llamadas del ejemplo son
-equivalentes. La segunda es lo que python realiza internamente al encontrarse
-el operador `in` o el operador `not in`.
-
-``` python
->>> 1 in [1,2,3]
-True
->>> [1,2,3].__contains__(1)
-True
-```
-
-### *Iterable*: `__next__` e `__iter__`
-
-El protocolo iterable permite crear objetos con los que es posible iterar en
-bucles `for` y otras estructuras. Por ejemplo, los archivos de texto en python
-soportan este protocolo, por lo que pueden leerse línea a línea en un bucle
-`for`.
-
-Igual que en el caso del protocolo `__len__`, que servía para habilitar la
-llamada a la función `len`, `__iter__` y `__next__` sirven, respectivamente,
-para habilitar las llamadas a `iter` y `next`.
-
-La función `iter` sirve para convertir el elemento a *iterable*, que es una
-clase que soporte el funcionamiento de la función `next`. Y `next` sirve para
-pasar al siguiente elemento de un iterable. Ejemplificado:
-
-``` python
->>> l = [1,2,3]
->>> next(l)
-Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
-TypeError: 'list' object is not an iterator
->>> it = iter(l)
->>> it
-<list_iterator object at 0x7ff745723908>
->>>
->>> next(it)
-1
->>> next(it)
-2
->>> next(it)
-3
->>> next(it)
-Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
-StopIteration
-```
-
-La función `__next__` tiene un comportamiento muy sencillo. Si hay un próximo
-elemento, lo devuelve. Si no lo hay lanza la excepción `StopIteration`, para
-que la capa superior la capture.
-
-Fíjate que la lista por defecto no es un iterable y que se debe construir un
-elemento iterable desde ella con `iter` para poder hacer `next`. Esto se debe a
-que la función `iter` está pensada para restaurar la posición del cursor en el
-primer elemento y poder volver a iniciar la iteración.
-
-Sorprendentemente, este es el procedimiento de cualquier `for` en python. El
-`for` es una estructura creada sobre un `while` que construye iterables e
-itera sobre ellos automáticamente.
-
-Este bucle `for`:
-
-``` python
-for el in secuencia:
- # hace algo con `el`
-```
-
-Realmente se implementa de la siguiente manera:
-
-``` python
-# Construye un iterable desde la secuencia
-iter_obj = iter(secuencia)
-
-# Bucle infinito que se rompe cuando `next` lanza una
-# excepción de tipo `StopIteration`
-while True:
- try:
- el = next(iter_obj)
- # hace algo con `el`
- except StopIteration:
- break
-```
-
-Así que, si necesitas una clase con capacidad para iterarse sobre ella, puedes
-crear un pequeño iterable que soporte el método `__next__` y devolver una
-instancia nueva de éste en el método `__iter__`.
-
-### *Creable*: `__init__`
-
-El método `__init__` es uno de los más usados e interesantes de esta lista, esa
-es la razón por la que ha aparecido en más de una ocasión durante este
-capítulo.
-
-El método `__init__` es a quién se llama al crear nuevas instancias de una
-clase y sirve para *ini*cializar las propiedades del recién creado objeto.
-
-Cuando se crean nuevos objetos, python construye su estructura en memoria,
-pidiéndole al sistema operativo el espacio necesario. Una vez la tiene, envía
-esa estructura vacía a la función `__init__` como primer argumento para que sea
-ésta la encargada de rellenarla.
-
-Como se ha visto en algún ejemplo previo, el método `__init__` (es un método,
-porque el objeto, aunque vacío, ya está creado) puede recibir argumentos de
-entrada adicionales, que serán los que la llamada al nombre de la clase reciba,
-a la hora de crear los nuevos objetos. Es muy habitual que el inicializador
-reciba argumentos de entrada, sobre todo argumentos con nombre, para que el
-programador que crea las instancias tenga la opción de inicializar los campos
-que le interesen.
-
-Volviendo a un ejemplo previo:
-
-``` python
-class Dog:
- type = "canine"
- def __init__(self, name):
- self.name = name
- def bark(self):
- print("Woof! My name is " + self.name)
-
-bobby = Dog("Bobby") # Aquí se llama a __init__
-```
-
-El nombre del perro, `"Bobby"` será recibido por `__init__` en el argumento
-`name` e insertado al `self` mediante `self.name = name`. De este modo, esa
-instancia de `Dog`, `bobby`, tomará el nombre `Bobby`.
-
-> NOTA: En muchas ocasiones, el método `__init__` inicializa a valores vacíos
-> todas las posibles propiedades del objeto con el fin de que quien lea el
-> código de la clase sea capaz de ver cuáles son los campos que se utilizan en
-> un primer vistazo. Es una buena práctica listar todos los campos posibles en
-> `__init__`, a pesar de que no se necesite inicializarlos aún, con el fin de
-> facilitar la lectura.
-
-> NOTA: Quien tenga experiencia con C++ puede equivocarse pensando que
-> `__init__` es un constructor. Tal y como se ha explicado anteriormente, al
-> método `__init__` ya llega un objeto construido. El objetivo de `__init__` es
-> inicializar. En python el constructor, que se encarga de crear las instancias
-> de la clase, es la función `__new__`.
-
-> NOTA: Si creas una clase a partir de la herencia y sobreescribes su método
-> `__init__` es posible que tengas que llamar al método `__init__` de la
-> superclase para inicializar los campos asociados a la superclase. Recuerda
-> que puedes acceder a la superclase usando `super`.
-
-### *Abrible* y *cerrable*: `__enter__` y `__exit__`
-
-Este protocolo permite que los objetos puedan ser abiertos y cerrados de forma
-segura y con una sintaxis eficiente. Aunque no se van a listar en profundidad,
-el objetivo de este punto es mostrar la sentencia `with` que se habilita
-gracias a estos protocolos y mostrar cómo facilitan la apertura y cierre.
-
-El PEP 343[^pep343] muestra en detalle la implementación de la sentencia
-`with`. Simplificándolo y resumiéndolo, `with` sirve para abrir elementos y
-cerrarlos de forma automática.
-
-> NOTA: Los PEP (*Python Enhancement Proposals*) son propuestas de mejora para
-> el lenguaje. Puedes consultar todos en la web de python. Son una fuente
-> interesante de información y conocimiento del lenguaje y de programación en
-> general.
-> <https://www.python.org/dev/peps/>
-
-Pensando en, por ejemplo, la lectura de un archivo, se requieren varias etapas
-para tratar con él, por ejemplo:
-
-``` python
-f = open("file.txt") # apertura del fichero
-f.read() # lectura
-f.close() # cierre
-```
-
-Este método es un poco arcaico y peligroso. Si durante la lectura del fichero
-ocurriera alguna excepción el fichero no se cerraría, ya que la excepción
-bloquearía la ejecución del programa. Para evitar estos problemas, lo lógico
-sería hacer una estructura `try-except` y añadir el cierre del fichero en un
-`finally`.
-
-La sentencia `with` se encarga básicamente de hacer eso y facilita la escritura
-de todo el proceso quedándose así:
-
-``` python
-with f as open("file.txt"): # apertura
- f.read() # en este cuerpo `f` está abierto
-
-# Al terminar el cuerpo, de forma normal o forzada,
-# `f` se cierra.
-```
-
-Ahora bien, para que el fichero pueda ser abierto y cerrado automáticamente,
-deberá tener implementados los métodos `__enter__` y `__exit__`. En el PEP 343
-se muestra la equivalencia entre la sentencia `with` y el uso de `__enter__`,
-`__close__` y el `try-except`.
-
-[^pep343]: Puedes leer el contenido completo del PEP en:
-<https://www.python.org/dev/peps/pep-0343/>
-
-### *Callable*: `__call__`
-
-Queda pendiente desde el capítulo sobre funciones, responder a lo que es un
-*callable* o *llamable*. Una vez llegados a este punto, tiene una respuesta
-fácil: un *llamable* es un objeto que soporta el protocolo correspondiente,
-definido por el método `__call__`.
-
-Aunque pueda parecer sorprendente, las funciones en python también se llaman de
-este modo, así que realmente son objetos que se llaman porque soportan este
-protocolo. Es lógico, porque las funciones, recuerda el capítulo previo, pueden
-guardar valores, como el contexto en el que se crean (*closure*). Las funciones
-son meros *llamables* y como tales se comportan.
-
-Llevado más allá, los tipos básicos de python están definidos en clases
-también, lógicamente, pero pueden ser llamados para hacer conversiones tal y
-como vimos en el capítulo sobre datos. Simplemente, soportan el protocolo
-*llamable*.
-
-``` python
->>> class Dog:
-... def __call__(self):
-... print("Dog called")
-...
->>> dog = Dog()
->>> dog()
-Dog called
-```
-
-Ten en cuenta que el método `__call__` puede recibir cualquier cantidad de
-argumentos como ya hemos visto en apartados anteriores, pero el primero será el
-propio objeto que está siendo llamado, el `self` que ya conocemos.
-
-Resumiendo, el método `__call__` describe cómo se comporta el objeto cuando se
-le aplican las paréntesis.
-
-### *Subscriptable*: `__getitem__`, `__setitem__` y `__delitem__`
-
-Tal y como el método anterior describía cómo se aplican las paréntesis a un
-objeto, el protocolo que se muestra en este apartado describe el comportamiento
-del objeto cuando se le aplican los corchetes. Recordando el capítulo sobre
-datos, los corchetes sirven para acceder a valores de las listas, tuplas,
-diccionarios y sets, que resultan ser también un tipo de objeto que describe
-este comportamiento mediante el protocolo que tenemos entre manos.
-
-Cuando python encuentra que se está tratando de acceder a un campo de un objeto
-mediante los corchetes llama automáticamente al método `__getitem__` y cuando
-se intenta asociar un campo a un valor llama al método `__setitem__` del
-objeto. Al pedir la eliminación de un campo del objeto con la sentencia `del`,
-se llama al método `__delitem__`.
-
-Aunque en otros protocolos aquí descritos hemos inventado un nombre para este
-documento, Python a este protocolo le denomina *subscriptable* así que cuando
-intentes acceder usando corchetes a un objeto que no soporta el protocolo, el
-error que saltará te utilizará la misma nomenclatura que nosotros.
-
-El siguiente ejemplo muestra el protocolo en funcionamiento en una clase sin
-funcionamiento alguno. Lo lógico y funcional sería utilizar estos dos métodos
-para facilitar el acceso a campos de estas clases o para crear clases que
-pudiesen sustituir a listas, tuplas, diccionarios o sets de forma sencilla.
-
-``` python
->>> class Dog:
-... def __getitem__(self, k):
-... print(k)
-... def __setitem__(self, k, v):
-... print(k, v)
-...
->>> bobby = Dog()
->>> bobby["field"]
-field
->>> bobby["field"] = 10
-field 10
-```
-
-Fíjate en que reciben diferente cantidad de argumentos de entrada cada uno de
-los métodos. El método `__setitem__` necesita indicar no sólo qué *item* desea
-alterarse, sino su también su valor.
-
-#### *Slice notation*
-
-Se trata de una forma avanzada de seleccionar las posiciones de un objeto, el
-nombre viene de *slice*, rebanada, y significa que puede coger secciones del
-objeto en lugar de valores únicos. Piénsalo como en una barra de pan cortada en
-rebanadas de la que quieres seleccionar qué rebanadas te interesan en bloque.
-
-No todos los objetos soportan *slicing*, pero los que lo hacen permiten acceder
-a grupos de valores en el orden en el que están indicando el inicio del grupo
-(inclusive), el final (no inclusive) y el salto de un elemento al siguiente.
-
-Además, los valores del *slice* pueden ser negativos. Añadir un número negativo
-al salto implica que el salto se hace hacia atrás. Añadirlo en cualquier de los
-otros dos valores, inicio o final de grupo, implica que se cuenta el elemento
-desde el final de la colección en dirección opuesta a la normal.
-
-La sintaxis de los *slice*s es la siguiente: `[inicio:fin:salto]`.
-Cada uno de los valores es opcional y si no se añaden se comportan de la
-siguiente manera:
-
-- Inicio: primer elemento
-- Fin: último elemento inclusive
-- Salto: un único elemento en orden de cabeza a cola
-
-> NOTA: El índice para representar el último elemento es -1, pero si se quiere
-> indicar como final, usar -1 descartará el último elemento porque el final no
-> es inclusivo. Para que sea inclusivo es necesario dejar el campo fin vacío.
-
-Dada una lista de los números naturales del 1 al 99, ambos incluidos, de
-nombre `l` se muestran unos casos de *slicing*.
-
-``` python
->>> l[-5:]
-[95, 96, 97, 98, 99]
->>> l[6:80:5]
-[6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56, 61, 66, 71, 76]
->>> l[60:0:-5]
-[60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5]
-```
-
-La sintaxis de los *slice*s mostrada sólo tiene sentido a la hora de acceder a
-los campos de un objeto, si se trata de escribir suelta lanza un error de
-sintaxis. Para crear *slice*s de forma separada se construyen mediante la
-clase `slice` de la siguiente manera: `slice(inicio, fin, salto)`.
-
-En los métodos del protocolo *subscriptable* (`__getitem__`, `__setitem__` y
-`__delitem__`) a la hora de elegir un *slice* se recibe una instancia del tipo
-*slice* en lugar de una selección única como en el ejemplo previo:
-
-``` python
->>> class Dog:
-... def __getitem__(self, item):
-... print(item)
-...
->>> bobby = Dog()
->>> bobby[1:100]
-slice(1, 100, None)
->>> bobby[1:100:9]
-slice(1, 100, 9)
->>> bobby[1:100:-9]
-slice(1, 100, -9)
-```
-
-Por complicarlo todavía más, los campos del *slice* creado desde la clase
-`slice` pueden ser del tipo que se quiera. El formato de los `:` es únicamente
-*sintactic sugar* para crear *slices* de tipo integer o string. Aunque después
-es responsabilidad del quien implemente el protocolo soportar el tipo de
-*slice* definido, es posible crear *slices* de lo que sea, incluso anidarlos.
-
-Como ejemplo de un caso que utiliza *slices* no integer, los tipos de datos
-como los que te puedes encontrar en la librería `pandas` soportan *slicing*
-basado en claves, como si de un diccionario se tratara.
-
-### Ejemplo de uso
-
-Para ejemplificar varios de estos protocolos, tomamos como ejemplo una pieza de
-código fuente que quien escribe este documento ha usado en alguna ocasión en su
-trabajo como desarrollador.
-
-Se trata de un iterable que es capaz de iterar en un sistema de ficheros
-estructurado en carpetas *año-mes-día* con la estructura `AAAA/MM/DD`. Este
-código se creó para analizar datos que se almacenaban de forma diaria en
-carpetas con esta estructura. Diariamente se insertaban fichero a fichero por
-un proceso previo y después se realizaban análisis semanales y mensuales de los
-datos. Esta clase permitía buscar por las carpetas de forma sencilla y obtener
-rápidamente un conjunto de carpetas que procesar.
-
-El ejemplo hace uso del módulo `datetime`, un módulo de la librería estándar
-que sirve para procesar fechas y horas. Por ahora, puedes ver la forma de
-importarlo como una receta y en el siguiente capítulo la entenderás a fondo. El
-funcionamiento del módulo es sencillo y puedes usar la ayuda para comprobar las
-funciones que no conozcas.
-
-Te animo a que analices el comportamiento del ejemplo, viendo en detalle cómo
-se comporta. Como referencia, fuera de la estructura de la clase, en las
-últimas líneas, tienes disponible un bucle que puedes probar a ejecutar para
-ver cómo se comporta.
-
-``` {.python .numberLines}
-from datetime import timedelta
-from datetime import date
-
-class dateFileSystemIterator:
-
- """
- Iterate over YYYY/MM/DD filesystems or similar.
- """
- def __init__( self, start = date.today(), end = date.today(),
- days_step = 1, separator = '/'):
- self.start = start
- self.current = start
- self.end = end
- self.separator = separator
- self.step = timedelta( days = days_step )
-
- def __iter__( self ):
- self.current = self.start
- return self
-
- def __next__( self ):
- if self.current >= self.end:
- raise StopIteration
- else:
- self.current += self.step
- datestring = self.current - self.step
- datestring = datestring.strftime( "%Y" \
- + self.separator \
- + "%m"+self.separator \
- +"%d")
- return datestring
-
- def __repr__( self ):
- out = self.current - self.step
- tostring = lambda x: x.strftime("%Y" \
- + self.separator \
- + "%m" \
- + self.separator + "%d")
- return "<dateFileSystemIterator: <Current: " \
- + tostring(self.current) + ">" \
- + ",<Start: " + tostring(self.start) + ">" \
- + ",<End: " + tostring(self.end) + ">" \
- + ",<Step: " + str(self.step) + ">"
-
-
-it = dateFileSystemIterator(start = date.today() - timedelta(days=30))
-print(it)
-for i in it:
- print(i)
-```
-
-#### Ejercicio libre: `yield` y los generadores
-
-La parte de la iteración del ejemplo previo puede realizarse forma más breve
-mediante el uso de la sentencia `yield`. Aunque no la trataremos, `yield`
-habilita muchos conceptos interesantes, entre ellos los *generadores*.
-
-A continuación tienes un ejemplo de cómo resolver el problema anterior mediante
-el uso de esta sentencia. Te propongo como ejercicio que investigues cómo
-funciona buscando información sobre los *generadores* (*generator*) y la
-propia sentencia `yield`.
-
-``` python
-from datetime import datetime, timedelta
-
-def iterate_dates( date_start, date_end=datetime.today(),
- separator='/', step=timedelta(days=1)):
- date = date_start
- while date < date_end:
- yield date.strftime('%Y'+separator+'%m'+separator+'%d')
- date += step
-```
-
-`yield` tiene mucha relación con las *corrutinas* (*coroutine*) que, aunque no
-se tratarán en este documento, son un concepto muy interesante que te animo a
-investigar. Si lo haces, verás que los generadores son un caso simple de una
-corrutina.
-
-## Lo que has aprendido
-
-Este capítulo también ha sido intenso como el anterior, pero te prometo que no
-volverá a pasar. El interés principal de este capítulo es el de hacerte conocer
-la programación orientada a objetos y enseñarte que en python lo inunda todo.
-Todo son objetos.
-
-Para entenderlo has comenzado aprendiendo lo que es la programación orientada a
-objetos, concretamente la orientada a clases, donde has visto por primera vez
-los conceptos de identidad propia, comportamiento y estado.
-
-Desde ahí has saltado al fundamento teórico de la programación orientada a
-objetos y has visitado la encapsulación, la herencia y el polimorfismo para
-luego, una vez comprendidos, comenzar a definir clases en python.
-
-Esto te ha llevado a necesitar conocer qué es el argumento que suele llamarse
-`self`, una excusa perfecta para definir qué son las variables y funciones de
-clase y en qué se diferencian de las propiedades y métodos.
-
-Como la encapsulación no se había tratado en detalle aún, lo próximo que has
-hecho ha sido zambullirte en los campos privados viendo cómo python los crea
-mediante un truco llamado *name mangling* y su impacto en la herencia.
-
-Aunque en este punto conocías el comportamiento general de la herencia hacia
-abajo, necesitabas conocerlo hacia arriba. Por eso, ha tocado visitar la
-función `super` en este punto, función que te permite acceder a la superclase
-de la clase en la que te encuentras. En lugar de contártela en detalle, se te
-ha dado una pincelada sobre ella para que tú investigues cuando lo veas
-necesario, pero que sepas por dónde empezar.
-
-Para describir más en detalle lo calado que está python de programación
-orientada a objetos necesitabas un ejemplo mucho más agresivo: los protocolos.
-A través de ellos has visto cómo python recoge las funcionalidades estándar y
-te permite crear objetos que las cumplan. Además, te ha servido para ver que
-**todo** en python es un objeto (hasta las clases lo son[^objects]) y para ver
-formas elegantes de resolver problemas comunes, como los iteradores, `with` y
-otros.
-
-También, te recuerdo que, aunque sea de forma colateral y sin prestarle
-demasiada atención, se te ha sugerido que cuando programamos no lo hacemos
-únicamente para nosotros mismos y que la facilidad de lectura del código y la
-preparación de éste para que otros lo usen es primordial. Los próximos
-capítulos tratan en parte de ésto: de hacer uso del patrimonio tecnológico de
-la humanidad, y de ser parte de él.
-
-[^objects]: Puedes preguntárselo a python:
- ``` python
- >>> class C: pass
- ...
-
- >>> isinstance(C, object)
- True
- ```
diff --git a/src/06_ejec_mod.md b/src/06_ejec_mod.md
deleted file mode 100644
index 0c54f4f..0000000
--- a/src/06_ejec_mod.md
+++ /dev/null
@@ -1,271 +0,0 @@
-# Módulos y ejecución
-
-Hasta ahora, has ejecutado el código en la REPL y de vez en cuando has usado
-`F5` en IDLE para ejecutar. Aunque te ha permitido salir del paso, necesitas
-saber más en detalle cómo funciona la ejecución para empezar a hacer tus
-programas. Además, es absurdo que te pelees contra todo, hay que saber qué
-batallas librar, así que necesitarás aprender a importar código realizado por
-otras personas para poder centrarte en lo que más te interesa: resolver tu
-problema.
-
-Este capítulo trata ambas cosas, que están muy relacionadas, y sirve como
-trampolín para el siguiente, la instalación de nuevos paquetes y la gestión de
-dependencias, y los posteriores sobre librerías interesantes que te facilitarán
-el desarrollo de tus proyectos.
-
-Este capítulo ya te capacita casi al cien por cien para la programación aunque
-aún no hemos trabajado su utilidad, pero seguro que alguna idea se te habrá
-ocurrido, si no no estarías leyendo este documento.
-
-## Terminología: módulos y paquetes
-
-En python a cualquier fichero de código (extensión `.py`) se le denomina
-*módulo* (*module*). A cualquier directorio que contenga módulos de código
-python y un fichero llamado `__init__.py`, que puede estar vacío, se le
-denomina *paquete* (*package*). El uso del fichero `__init__.py` permite que
-python busque módulos dentro del directorio.
-
-Piensa en los paquetes como grupos de módulos que incluso pueden anidarse con
-subpaquetes. En la documentación oficial verás en más de una ocasión que se
-trata a los paquetes como si fueran módulos, y tiene cierto sentido, porque,
-normalmente, existe un módulo principal que permite el acceso a un subpaquete.
-Así que, principalmente estás trabajando con un único módulo de un paquete, que
-contiene subpaquetes a los que este módulo hace referencia. Aunque pueda sonar
-algo enrevesado, no te preocupes por ahora: los módulos son ficheros únicos y
-los paquetes conjuntos de ellos.
-
-## Ejecución
-
-Ya conoces un par de maneras de ejecutar tus módulos de python. Usar la REPL,
-introduciéndole el código que quieres ejecutar, llamar a la función «ejecutar
-módulo» de IDLE con la tecla `F5` e incluso llamar a la shell de sistema con un
-comando similar a este:
-
-``` bash
-python mi_archivo.py
-```
-
-Normalmente, los ficheros de python se ejecutan de este último modo en
-producción, mientras que los dos anteriores son más usados a la hora de
-desarrollar. Realmente son métodos similares, en todos ellos el intérprete
-accede al fichero o contenido que recibe y ejecuta las líneas una a una.
-
-Existe también una opción adicional muy usada que sirve para ejecutar módulos
-que el sistema sea capaz de encontrar por sí mismo, en lugar de indicarle la
-ruta, se le puede indicar simplemente el nombre del módulo usando la opción
-`-m`:
-
-``` bash
-python -m nombre_de_modulo
-```
-
-## Importación y *namespaces*
-
-Anteriormente hemos pasado sobre la sintaxis de la importación de forma muy
-superficial pero tampoco es mucho más compleja a lo que ha aparecido. La
-sentencia `import` permite importar diferentes módulos a nuestro programa como
-en el siguiente ejemplo:
-
-``` python
->>> import datetime
->>> datetime
-<module 'datetime' from '/usr/lib/python3.6/datetime.py'>
-```
-
-Han pasado, sin embargo, muchas cosas interesantes en el ejemplo. En primer
-lugar, python ha buscado y encontrado el módulo `datetime` en el sistema y, en
-segundo lugar, ha creado un objeto módulo llamado `datetime` que atesora todas
-las definiciones globales del módulo `datetime`.
-
-Empezando por el final, python usa lo que se conoce como *namespace* de forma
-muy extendida. Los *namespaces*, de nombre (*name*) y espacio (*space*), son
-una herramienta para separar contextos ampliamente usada. Los objetos, en
-realidad, son una caso de *namespace* ya que cuando se llama a un método se le
-dice cuál es el contexto de la llamada, es decir: al método de qué objeto se
-llama.
-
-Para los módulos el proceso es el mismo. La sentencia `import` trae un módulo
-al programa pero lo esconde tras su *namespace*, de este modo, para acceder a
-algo definido en el recién importado módulo es necesario indicarle el nombre de
-éste de la siguiente manera:
-
-``` python
->>> import datetime
->>> datetime.date.today()
-datetime.date(2019, 12, 3)
-```
-
-En el ejemplo se accede a la clase `date` dentro del módulo `datetime`, y se
-lanza su función `today`, que indica el día de hoy. Como puedes apreciar, el
-operador `.` se utiliza del mismo modo que en las clases y objetos, y en
-realidad es difícil saber cuándo se está accediendo a una clase y cuándo a un
-módulo, aunque tampoco es necesario saberlo.
-
-Para no tener que escribir el nombre del módulo completo, existe otra versión
-de la sentencia `import` que tiene un comportamiento muy similar:
-
-``` python
->>> import datetime as dt
->>> dt.date.today()
-datetime.date(2019, 12, 3)
-```
-
-En este ejemplo, se ha cambiado el nombre del módulo a uno más corto decidido
-por el programador, `dt`. El funcionamiento es el mismo, simplemente se ha
-cambiado el nombre para simplificar. Este cambio de nombre también es útil
-cuando se va a importar un módulo cuyo nombre es igual que alguna otra
-definición. Cambiando el nombre se evitan colisiones.
-
-Existen versiones además, que permiten importar únicamente las funciones y
-clases seleccionadas, pero que las añaden al *namespace* actual, para evitar
-tener que usar el prefijo.
-
-``` python
->>> from datetime import date
->>> date.today()
-datetime.date(2019, 12, 3)
-```
-
-En este último ejemplo, se trae la clase `date` al contexto actual. También
-existe la posibilidad de importar más de una definición del módulo, usando la
-coma para separarlas, o todo lo que el módulo exponga mediante el símbolo `*`.
-Es peligroso, sin embargo, traer definiciones al namespace actual de forma
-descuidada, sobre todo con la última opción, porque, es posible que se repitan
-nombres por accidente y se pisen definiciones. Los namespaces se inventan con
-el fin de separar las definiciones y evitar colisiones de este tipo.
-
-### Búsqueda
-
-Una vez descrito cómo se interactúa con los módulos importados, es necesario
-describir dónde se buscan estos módulos.
-
-Los módulos se buscan en los siguientes lugares:
-
-1. El directorio del fichero ejecutado o el directorio de trabajo de la REPL
-2. Los directorios indicados en el entorno.
-3. La configuración por defecto (depende de la instalación)
-
-Esto significa que si guardas un archivo de python en IDLE y guardas otro más
-en el mismo directorio con el nombre `modulo.py` podrás importarlo usando
-`import modulo` en el primero, ya que comparten directorio. Lo mismo ocurre con
-los paquetes, crear un directorio con nombre `paquete` y añadirle un fichero
-vacío llamado `init.py` te permitirá hacer `import paquete`. Si añadieras más
-módulos dentro del paquete, podrías importar cada uno de ellos mediante
-`paquete.modulo`.
-
-> NOTA: Los nombres de los ficheros deben coincidir con el el nombre del módulo
-> más la extensión `.py`. En el caso de los directorios, saltar a un
-> subdirectorio implica acceder a un paquete, por lo que se añadirá un punto
-> (`.`).
-
-El primer punto sirve para facilitar que organices tu proyecto de python en
-varios módulos, separando así la funcionalidad en diferentes archivos.
-
-Los últimos dos puntos son los que permiten a python encontrar su librería
-estándar y los módulos de sistema. El tercero depende de la instalación y del
-formato de ésta: si python está instalado como portable no será igual que si se
-instala en el sistema del modo habitual. El segundo punto también puede variar
-de un sistema a otro, pero en resumen se trata de varias variables de entorno
-de sistema que le indican a python dónde buscar (normalmente toman el nombre
-`PYTHONPATH`, pero no es siempre así). El segundo punto puede alterarse de modo
-que en función de lo que se le indique, se puede pedir a python que busque los
-módulos en un lugar u otro.
-
-Estos lugares de búsqueda se pueden mostrar de la siguiente manera:
-
-``` python
->>> import sys
->>> print(sys.path)
-[ '',
- '/usr/lib/python36.zip',
- '/usr/lib/python3.6',
- '/usr/lib/python3.6/lib-dynload',
- '/usr/local/lib/python3.6/dist-packages',
- '/usr/lib/python3/dist-packages']
-```
-
-En función del sistema en el que te encuentres y la configuración que tengas,
-python mostrará diferente resultado.
-
-Rescatando un ejemplo previo:
-
-``` python
->>> import datetime
->>> datetime
-<module 'datetime' from '/usr/lib/python3.6/datetime.py'>
-```
-
-Ahora entiendes por qué es capaz de encontrar `datetime` en
-`/usr/lib/python3.6`, carpeta listada en `sys.path`, bajo el nombre
-`datetime.py`.
-
-## Ejecución vs Importación: `__main__` *guard*
-
-A la hora de importar un módulo, python procesa el contenido de éste ya que
-necesita definir las funciones, clases, valores, etc. a exportar: ejecuta el
-módulo.
-
-Python define una forma de separar la funcionalidad del código de sus
-definiciones con el fin de poder crear código cuyas definiciones sean
-reutilizables mediante la importación en otro módulo, sin que tenga ninguna
-funcionalidad cuando esto ocurra, pero habilitando que tenga funcionalidades
-cuando sea llamado directamente.
-
-Un ejemplo de uso de esto puede ser un módulo de acceso a ficheros, por
-ejemplo, que visualice el contenido del fichero cuando se llame de forma
-directa pero que cuando se importe únicamente aporte las funciones de lectura y
-escritura sin leer y mostrar ningún fichero.
-
-Para que los módulos puedan tener esta doble vida, python define la variable
-`__name__` que representa en qué nivel del *scope* se está ejecutando el módulo
-actual. La variable `__name__` toma el valor del nombre del módulo cuando éste
-está siendo importado y el valor `__main__` cuando ha sido llamado de forma
-directa o está siendo ejecutado en la REPL. `__main__` es el *scope* global de
-los programas, por lo que cuando algo se declara en él, implica que es el
-programa principal.
-
-Para poder diferenciar cuándo se ha ejecutado un módulo de forma directa y
-cuando se ha importado se utiliza lo que se conoce como `__main__` *guard*:
-
-``` python
-if __name__ == "__main__":
- # Este bloque sólo se ejecuta cuando el módulo es el principal
-```
-
-Aunque igual es un poco incómodo de entender de primeras, encontrarás esta
-estructura en casi cualquier módulo de código python. Se utiliza
-constantemente, incluso para los casos en los que no se pretende que el código
-pueda importarse. Es una buena práctica incluir el *guard* para separar la
-ejecución de las definiciones, de este modo, quien quiera saber cuál es la
-funcionalidad del módulo tendrá mucho más fácil la búsqueda.
-
-Puedes leer más sobre este tema en la documentación de python[^main-guard].
-
-[^main-guard]: <https://docs.python.org/3/library/__main__.html>
-
-
-Siguiendo este concepto, también existe el un estándar de nomenclatura de
-ficheros. El nombre `__main__.py` hace referencia al fichero que contiene el
-código que se incluiría dentro del *guard* y será el fichero que python buscará
-ejecutar siempre que se le pida ejecutar un paquete o un directorio sin
-especificar qué módulo debe lanzar. Por ejemplo, ejecutar `python .`[^dot] en
-la shell de sistema es equivalente a ejecutar `python __main__.py`.
-
-[^dot]: `.` significa directorio actual en cualquiera de los sistemas
- operativos comunes.
-
-
-## Lo que has aprendido
-
-En este capítulo corto has aprendido lo necesario sobre importación de módulos
-y ejecución de código. Conocer en detalle el patrón de búsqueda de módulos de
-python es primordial para evitar problemas en el futuro y organizar los
-proyectos de forma elegante.
-
-Además, el `__main__` *guard* era una de las últimas convenciones de uso común
-que quedaban por explicar y una vez vista ya eres capaz de leer proyectos de
-código fuente que te encuentres por ahí sin demasiados problemas.
-
-Los próximos capítulos, basándose en lo aprendido en éste, te mostrarán cómo
-instalar nuevas dependencias y cómo preparar tu propio código para que pueda
-ser instalado de forma limpia y elegante.
diff --git a/src/07_install.md b/src/07_install.md
deleted file mode 100644
index 3da8ae0..0000000
--- a/src/07_install.md
+++ /dev/null
@@ -1,243 +0,0 @@
-# Instalación y dependencias
-
-Ahora que sabes lidiar con módulos, necesitas aprender a instalarlos en tu
-propio sistema, porque es bastante tedioso que tengas que copiar el código de
-todas tus dependencias en la carpeta de tu proyecto.
-
-En la introducción no aseguramos de instalar con python la herramienta `pip`.
-Que sirve para instalar paquetes nuevos en el sistema de forma sencilla.
-
-## Funcionamiento de `pip`
-
-`pip` es una herramienta extremadamente flexible, capaz de instalar módulos de
-python de diferentes fuentes: repositorios de git, carpetas de sistema o, la
-más interesante quizás de todas, el *Python Package Index* (*PyPI*)[^pypi].
-
-[^pypi]: https://pypi.org/
-
-`pip` buscará la descripción del paquete en la fuente que se le indique y, de
-esta descripción, obtendrá las dependencias necesarias y las indicaciones de
-cómo debe instalarlo. Una vez procesadas las normas, procederá a instalar el
-paquete en el directorio de sistema con todas las dependencias de éste para que
-funcione correctamente.
-
-Una vez instalado el paquete en el directorio de sistema está listo para ser
-importado.
-
-### PyPI
-
-El *Python Package Index* o *PyPI* es un repositorio que contiene software
-programado en python. En él se listan miles de librerías creadas por
-programadores de python para que cualquiera pueda descargarlas e instalarlas.
-Más adelante veremos algunas de ellas y nos acostumbraremos a usarl PyPI como
-recurso.
-
-Ahora que sabes programar en python tú también puedes publicar tus proyectos
-ahí para que otras personas los usen para crear los suyos.
-
-### Reglas de instalación: `setuptools` y `setup.py`
-
-Para que `pip` pueda hacer su trabajo correctamente hay que indicarle cómo debe
-hacerlo, ya que cada paquete es un mundo y tiene necesidades distintas. El
-módulo `setuptools` permite crear un conjunto de reglas comprensible por `pip`
-que facilita la distribución e instalación.
-
-Normalmente este conjunto de reglas suele almacenarse en un fichero de nombre
-`setup.py`. Como ejemplo de `setup.py` puedes ver el de la librería
-`BeautifulSoup4`[^bs4-setup] que se adjunta a continuación con ligeras
-alteraciones para que encaje en la página:
-
-[^bs4-setup]: https://www.crummy.com/software/BeautifulSoup/bs4/doc/
-
-``` {.python .numberLines}
-from setuptools import (
- setup,
- find_packages,
-)
-
-with open("README.md", "r") as fh:
- long_description = fh.read()
-
-setup(
- name="beautifulsoup4",
- # NOTE: We can't import __version__ from bs4 because bs4/__init__.py
- # is Python 2 code, and converting it to Python 3 means going through
- # this code to run 2to3.
- # So we have to specify it twice for the time being.
- version = '4.8.1',
- author="Leonard Richardson",
- author_email='leonardr@segfault.org',
- url="http://www.crummy.com/software/BeautifulSoup/bs4/",
- download_url = "http://www.crummy.com/software/BeautifulSoup/bs4/download/",
- description="Screen-scraping library",
- install_requires=["soupsieve>=1.2"],
- long_description=long_description,
- long_description_content_type="text/markdown",
- license="MIT",
- packages=find_packages(exclude=['tests*']),
- extras_require = {
- 'lxml' : [ 'lxml'],
- 'html5lib' : ['html5lib'],
- },
- use_2to3 = True,
- classifiers=[
- "Development Status :: 5 - Production/Stable",
- "Intended Audience :: Developers",
- "License :: OSI Approved :: MIT License",
- "Programming Language :: Python",
- "Programming Language :: Python :: 2.7",
- 'Programming Language :: Python :: 3',
- "Topic :: Text Processing :: Markup :: HTML",
- "Topic :: Text Processing :: Markup :: XML",
- "Topic :: Text Processing :: Markup :: SGML",
- "Topic :: Software Development :: Libraries :: Python Modules",
- ],
-)
-```
-
-En el ejemplo se aprecia a la perfección el tipo de información que es
-necesario aportarle a `setuptools`. Dependiendo del proyecto, esta
-configuración puede ser más compleja o más sencilla, y tendrás que indagar a
-través de la configuración de `setuptools` para ajustar la herramienta a tu
-proyecto.
-
-
-## Entornos virtuales y dependencias: `pipenv`
-
-La herramienta `pip` es interesante para instalar herramientas en el sistema
-pero tiene ciertas carencias. La primera, que no es capaz de resolver las
-dependencias a la hora de desinstalar paquetes, por lo que, si se instala un
-paquete que dependa de otro paquete, `pip` instalará todas las dependencias
-necesarias, pero por miedo a romper paquetes, no las desinstalará si se le pide
-desinstalar el paquete que las arrastró.
-
-Por otro lado, si quieres trabajar en proyectos de desarrollo, probablemente
-tengas que instalar sus dependencias. Si tienes varios proyectos en marcha
-simultáneamente o si tus sistema necesita de alguna herramienta escrita en
-python, es posible que tengas colisiones.
-
-Imagina que dos de los proyectos, por ejemplo, usan versiones diferentes de una
-de sus librerías. Si instalas sus dependencias usando `pip`, se mezclaran en tu
-sistema y no podrán coexistir. Además, cuando termines los proyectos o
-abandones su desarrollo, te interesará limpiar sus dependencias de tu sistema,
-cosa complicada si `pip` no gestiona la liberación de paquetes de forma
-correcta.
-
-Para evitar estos problemas y algún otro adicional, existen herramientas
-adicionales que alteran el comportamiento de `pip` y del propio python, creando
-lo que se conoce como *entornos virtuales* (*virtual environments*) que quedan
-aislados entre ellos y el sistema.
-
-El funcionamiento de los entornos virtuales es muy sencillo. Cuando se activan,
-crean un nuevo contexto en el que alteran las variables de entorno que
-describen dónde debe buscarse el intérprete de python, dónde busca éste los
-módulos y dónde instala `pip` los paquetes. Cuando se desactivan, se restaura
-el entorno por defecto. De este modo, cada entorno virtual queda perfectamente
-aislado de otros o incluso de la instalación del sistema, permitiéndote hasta
-tener diferentes versiones de python en tu sistema y que no colisionen entre
-ellas. Cuando has terminado con tu entorno virtual puedes borrarlo de forma
-segura sabiendo que no va a afectar a tu sistema y que va a llevarse todas las
-dependencias con él.
-
-Históricamente se han utilizado varias herramientas para esta labor, como
-`virtualenv`, que como era poco amigable se simplificaba con
-`virtualenv-wrapper` u otras. Hoy en día `pipenv` es la herramienta
-recomendada.
-
-`pipenv` es una combinación de `virtualenv` y `pip` creada para gestionar
-entornos virtuales y dependencias de desarrollo. Puedes considerarla un gestor
-de paquetes de desarrollo como `npm` en JavaScript, `composer` en PHP o
-cualquier otro que conozcas. Aporta la mayor parte de funcionalidades
-habituales como ficheros de dependencias, lockfiles etc. mientras que expone
-una interfaz de comandos sencilla y bien documentada.
-
-### Instalación
-
-Para instalar pipenv, podemos usar `pip`, que instalamos en la introducción.
-En la shell de sistema ejecutando:
-
-``` bash
-pip install pipenv
-```
-
-> NOTA: en función del sistema que utilices, puede que `pip` se llame
-> `pip3`. El funcionamiento es idéntico.
-
-### Uso
-
-Una vez instalado `pipenv`, puedes usarlo en el directorio que desees para
-crear un conjunto de nuevas dependencias pidiéndole que instale un nuevo
-paquete lanzando la orden `pipenv install` en la shell de sistema.
-
-Para ejecutar módulos en el entorno virtual recién creado dispones de dos
-opciones: `pipenv shell` que prepara una shell de sistema en el entorno o
-`pipenv run` que ejecuta el comando que se le envíe en el entorno.
-
-Puedes seguir añadiendo dependencias al proyecto con `pipenv install` y
-eliminar las que no te gusten con `pipenv uninstall`. Además, dispones de
-muchas opciones adicionales que te animo que ojees ejecutando `pipenv --help`.
-
-
-### Usar IDLE desde un entorno virtual
-
-Si utilizas entornos virtuales deberás preparar IDLE para verlos y que su REPL
-y su intérprete encuentren los paquetes del entorno virtual.
-
-Puedes lanzar IDLE desde el entorno virtual usando el siguiente truco en la
-shell de sistema:
-
-``` bash
-pipenv run python -m idlelib.idle
-```
-
-Recordando el capítulo anterior y lo descrito en este, ese comando ejecuta
-`python -m idlelib.idle` en el entorno virtual en el que te encuentres. El
-comando, por su parte le pide a python que ejecute el módulo `idle` del paquete
-`idlelib`, que contiene el propio programa IDLE.
-
-Si trabajas con otros editores integrados de código tendrás que aprender a
-hacer que sus intérpretes busquen en el entorno virtual actual, pero casi todos
-los editores actuales soportarán esta opción de una forma u otra.
-
-## Otras herramientas
-
-La realidad es que las herramientas propuestas no son las únicas que existen
-para estas tareas. Históricamente, python ha tenido otras herramientas con
-comportamientos similares a `pip`, como `easy_install` y otras con parecidas a
-`setuptools` como `distutils` y otras que asemejan a `pipenv`. Este capítulo
-trata únicamente las herramientas que se consideran las recomendadas en el
-momento en el que se está escribiendo[^recommended-tools], aunque los conceptos
-descritos son extrapolables, no sólo a otras herramientas sino también a otros
-lenguajes y entornos.
-
-[^recommended-tools]: Para un listado más extenso, visitar:
-<https://packaging.python.org/guides/tool-recommendations/>
-
-
-## Lo que has aprendido
-
-En este capítulo has aprendido lo necesario sobre las herramientas que rodean
-a python y su uso. De este modo, no te vas a sentir perdido en un maremágnum de
-nombres extraños y comandos cuando trabajes en proyectos que ya las usan.
-
-Más que convertirte en un experto de cómo trabajar con estas herramientas, cosa
-que te dará la práctica, este episodio te ha dado las referencias que necesitas
-para ir creciendo por tu cuenta en su uso, explicándote el interés de cada una
-de ellas.
-
-Te encontrarás, probablemente, proyectos que utilicen las herramientas de un
-modo peculiar, usen otras herramientas o hasta proyectos que no las usen
-correctamente. Este es un mundo complejo, y la historia de python no facilita
-la elección. Durante mucho tiempo la comunidad creó nuevas herramientas
-para suplir las carencias de otras y el ecosistema se complicó. A pesar de que
-hoy en día se recomiende el uso de unas herramientas sobre otras, no siempre
-fue así y muchos proyectos se han quedado obsoletos en este sentido.
-
-Para evitar atarte a las herramientas que aquí se muestran, se ha preferido
-darte una ligera noción, haciéndote recordar que las herramientas no son
-realmente lo importante, sino el conocimiento subyacente.
-
-Con la herramienta principal que has conocido en este apartado, `pipenv`,
-podrás instalar los paquetes que quieras de forma aislada y jugar con ellos
-todo lo que desees sin manchar tu sistema por lo que podrás adentrarte en los
-próximos capítulos sin miedo a estropear tu pulcra configuración inicial.
diff --git a/src/08_stdlib.md b/src/08_stdlib.md
deleted file mode 100644
index fbaa974..0000000
--- a/src/08_stdlib.md
+++ /dev/null
@@ -1,372 +0,0 @@
-# La librería estándar
-
-La librería estándar se refiere a todas las utilidades que un lenguaje de
-programación trae consigo. Los lenguajes de programación, a parte de aportar la
-propia funcionalidad del lenguaje en sí mismo que simplemente sería la
-ejecución del código fuente que se le indique, suelen incluir funcionalidades
-que no están necesariamente relacionadas con ese proceso.
-
-El motivo de esto es facilitar el uso del lenguaje para las labores más
-comunes, incluyendo el código necesario para realizarlas sin necesitar añadidos
-externos.
-
-En lenguajes de programación de dominio específico la librería estándar suele
-contemplar muchos casos de ese dominio concreto. Por ejemplo, el lenguaje de
-programación Julia, aunque esté diseñado con el objetivo de ser un lenguaje
-válido para cualquier uso, su área de trabajo se centra en el entorno
-científico. Es por eso que incluye en su librería estándar paquetes de álgebra
-lineal, estadística y otros.
-
-Existen muchas diferentes aproximaciones a la librería estándar. Algunos
-lenguajes las mantienen extremadamente reducidas con el fin de que la
-implementación del lenguaje sea más liviana, con la contrapartida de forzar a
-quien los use a tener que utilizar herramientas externas o a desarrollarlas.
-
-Python es un lenguaje de propósito general con una extensísima librería
-estándar. Esto dificulta la selección de apartados a mostrar en este capítulo,
-pero facilita la programación de cualquier aplicación en éste lenguaje.
-
-Los paquetes estándar de python facilitan la lectura de infinidad de tipos de
-fichero, la conversión y el tratamiento de datos, el acceso a la red, la
-ejecución concurrente, etc. por lo que librería estándar es más que suficiente
-para muchas aplicaciones y no se requiere añadir módulos externos.
-
-Conocer la librería estándar y ser capaz de buscar en ella los paquetes que
-necesites te facilitará mucho la tarea, evitando, por un lado, que dediques
-tiempo a desarrollar funcionalidades que el propio lenguaje ya aporta y, por
-otro, que instales paquetes externos que no necesitas.
-
-Todos los apartados aquí listados están extremadamente bien documentados en la
-página oficial de la documentación de python y en la propia ayuda. Eso sí,
-tendrás que importarlos para poder leer la ayuda, pero ya sabes cómo se hace.
-
-A continuación se recogen los módulos más interesantes, aunque en función del
-proyecto puede que necesites algún otro. Puedes acceder al listado completo en
-la página oficial de la documentación[^stdlibdoc].
-
-[^stdlibdoc]: <https://docs.python.org/3/library/index.html#library-index>
-
-
-## Interfaz al sistema operativo: `os`
-
-Ya definimos previamente el concepto *interfaz* como superficie de contacto
-entre dos entidades. Esta librería facilita la interacción entre python y el
-sistema operativo.
-
-La comunicación con el sistema operativo es primordial para cualquier lenguaje
-de programación de uso general, ya que es necesario tener la capacidad de hacer
-peticiones directas al sistema operativo. Por ejemplo, cambiar de directorio
-actual para facilitar la inclusión rutas relativas en el programa, resolver
-rutas a directorios y un largo etc.
-
-En capítulos previos hemos hablado de las diferencias entre sistemas
-operativos. Esta librería, además, facilita el trabajo para esos casos. Por
-ejemplo, que el separador de directorios en UNIX es `/` y en Windows `\`, si
-quisiéramos programar una aplicación multiplataforma, nos encontraríamos con
-problemas. Sin embargo, el módulo `os.path` dispone de herramientas para
-gestionar las rutas de los directorios por nosotros, por ejemplo, la variable
-`os.path.sep` (copiada en `os.sep` por abreviar), guarda el valor del separador
-del sistema en el que se esté ejecutando la aplicación: `/` en UNIX y `\` en
-Windows.
-
-Este paquete es muy interesante para desarrollar código portable entre las
-diferentes plataformas.
-
-## Funciones relacionadas con el intérprete: `sys`
-
-Aunque el nombre de este módulo suene complicado, su uso principal es el de
-acceder a funcionalidades que el propio python controla. Concretamente, se usa
-sobre todo para la recepción de argumentos de entrada al programa principal,
-la redirección de entradas y salidas del programa y la terminación del
-programa.
-
-### Salida forzada: `sys.exit()`
-
-Para salir de forma abrupta del programa y terminar su ejecución, python
-facilita la función `sys.exit()`. Al ejecutarla la
-
-### *Standard streams*: `sys.stdin`, `sys.stdout` y `sys.stderr`
-
-Cuando se trabaja en programas que funcionan en la terminal se pueden describir
-tres vías de comunicación con el usuario:
-
-1. El teclado: de donde se obtiene lo que el usuario teclee mediante la función
- `input`.
-2. La pantalla: a donde se escribe al ejecutar la función `print`.
-3. Y la salida de errores: Una salida especial para los fallos, similar a la
- anterior, pero que se diferencia para poder hacer un tratamiento distinto y
- porque tiene ciertas peculiaridades distintas. Los mensajes de error de
- excepciones se envían aquí.
-
-Estas tres vías se conocen comúnmente como entrada estándar (*standard input*),
-`stdin`, salida estándar (*standard output*), `stdout`, y error estándar
-(*standard error*), `stderr`. Este concepto responde al nombre de *standard
-streams* y es una abstracción de los dispositivos físicos que antiguamente se
-utilizaban para comunicarse con una computadora. Hoy en día, estos tres
-*streams* son una abstracción de esa infraestructura y se comportan como
-simples ficheros de lectura en el primer caso y de escritura en los otros dos.
-
-Los diferentes sistemas operativos los implementan a su modo, pero desde el
-interior de python son simplemente ficheros abiertos, como si se hubiese
-ejecutado la función `open` en ellos y ellos se encargan de leer o escribir a
-la vía de interacción con el usuario correspondiente. Es decir, en realidad
-`print`, `input`, etc. son únicamente funciones que facilitan la labor de
-escribir manualmente en estos *streams*:
-
-``` python
->>> import sys
->>> chars = sys.stdout.write("Hola\n")
-Hola
->>> chars # Recoge el número de caracteres escritos
-5
-```
-
-La realidad es que estos *streams* dan una flexibilidad adicional muy
-interesante. Como son únicamente variables de ficheros abiertos pueden
-cerrarse y sustituirse por otros, permitiéndote redireccionar la entrada o la
-salida de un programa a un fichero.
-
-El siguiente ejemplo redirecciona la salida de errores a un fichero llamado
-`exceptions.txt` y trata de mostrar una variable que no está definida. Python
-en lugar de mostrar el mensaje de una excepción, la escribe en el fichero al
-que se ha redireccionado la salida:
-
-``` python
->>> sys.stderr = open("exceptions.txt", "w")
->>> aasda # No muestra el mensaje de error
->>>
->>> with open("exceptions.txt") as f:
-... f.read() # Muestra el contenido del archivo
-...
-'Traceback (most recent call last):\n File "<stdin>", line 1, in \
-<module>\nNameError: name \'aasda\' is not defined\n'
-```
-
-
-### Argumentos de entrada: `sys.argv`
-
-Es posible añadir argumentos de entrada a la ejecución de los programas. Piensa
-en el propio programa llamado python, al ejecutarlo en la shell de sistema se
-le pueden mandar diferentes opciones que, por ejemplo, digan qué módulo debe
-ejecutar:
-
-``` bash
-python test.py
-```
-
-Para la shell de sistema, todo lo que se escriba después de la primera palabra
-es considerado un argumento de entrada.
-
-Cuando se ejecutan nuestros programas escritos en python, es posible también
-enviarles argumentos de entrada:
-
-``` bash
-python test.py argumento1 argumento2 ...
-```
-
-Lo que el python reciba después del nombre del módulo a ejecutar lo considerará
-argumentos de entrada de nuestro módulo y nos lo ordenará y dejará disponible
-en la variable `sys.argv`, una lista de todos los argumentos de entrada.
-
-Si muestras el contenido de la variable en la REPL, te responderá `['']` ya que
-la REPL se ejecuta sin argumentos. Sin embargo, si creas un módulo y le añades
-este contenido:
-
-``` python
-import sys
-print(sys.argv)
-```
-
-Verás que se imprime una lista con el nombre del archivo en su primer elemento.
-
-Si ejecutas el módulo desde la shell de sistema añadiéndole argumentos de
-entrada:
-
-``` bash
-python modulo.py arg1 arg2 arg3
-```
-
-La lista `sys.argv` recibirá todos, siempre a modo de string.
-
-Los argumentos de entrada son extremadamente interesantes para permitir que los
-programas sean configurables por quien los use sin necesidad de tener que
-editar el código fuente, cosa que nunca debería ser necesaria a menos que
-exista un error en éste o quiera añadirse una funcionalidad.
-
-## Procesamiento de argumentos de entrada: `argparse`
-
-Como la variable `sys.argv` entrega los argumentos de entrada tal y como los
-recibe y no comprueba si son coherentes, python dispone de una librería
-adicional para estas labores. El módulo `argparse` permite definir qué tipo de
-argumentos de entrada y opciones tiene tu programa y qué reglas deben seguir.
-Además, facilita la creación de ayudas como la que se muestra cuando ejecutas
-`python -h` o `pip -h` en la shell de tu sistema.
-
-Es una librería bastante compleja con infinidad de opciones que es mejor que
-leas en la propia documentación cuando necesites utilizarla.
-
-## Expresiones regulares: `re`
-
-Las expresiones regulares (*regular expression*, también conocidas como
-*regexp* y *regex*) son secuencias de caracteres que describen un patrón de
-búsqueda.
-
-En python se soportan mediante el módulo `re` de la librería estándar.
-
-## Matemáticas y estadística: `math` y `statistics`
-
-El módulo `math` soporta gran cantidad de operaciones matemáticas avanzadas
-para coma flotante. En él puedes encontrar logaritmos, raíces, etc.
-
-El módulo `statistics` soporta estadística básica como medias, medianas,
-desviación típica, etc.
-
-Ambos módulos tienen mucho interés ya que python se usa extensivamente en el
-análisis de datos. Aunque tiene librerías de terceros mucho más adecuadas para
-esta labor, para proyectos pequeños puede que sea suficiente con estos módulos.
-
-## Protocolos de internet: `urllib`
-
-`urllib` es un conjunto de paquetes que permiten seguir URLs o enlaces,
-principalmente para HTTP y su versión segura HTTPs. Soporta cookies,
-redirecciones, autenticación básica, etc.
-
-El siguiente ejemplo te muestra cómo descargar el primer boceto del estándar
-del protocolo HTTP de la página web del IETF. Recordando los apartados previos,
-fíjate en el uso de la sentencia `with` y en el uso de los corchetes para
-obtener un número limitado de caracteres de la recién decodificada respuesta.
-
-``` python
->>> from urllib.request import urlopen
->>> with urlopen("https://tools.ietf.org/rfc/rfc2068.txt") as resp:
-... print( resp.read().decode("utf-8")[:1750] )
-...
-'
-
-
-
-
-
-Network Working Group R. Fielding
-Request for Comments: 2068 UC Irvine
-Category: Standards Track J. Gettys
- J. Mogul
- DEC
- H. Frystyk
- T. Berners-Lee
- MIT/LCS
- January 1997
-
-
- Hypertext Transfer Protocol -- HTTP/1.1
-
-Status of this Memo
-
- This document specifies an Internet standards track protocol for the
- Internet community, and requests discussion and suggestions for
- improvements. Please refer to the current edition of the "Internet
- Official Protocol Standards" (STD 1) for the standardization state
- and status of this protocol. Distribution of this memo is unlimited.
-
-Abstract
-
- The Hypertext Transfer Protocol (HTTP) is an application-level
- protocol for distributed, collaborative, hypermedia information
- systems. It is a generic, stateless, object-oriented protocol which
- can be used for many tasks, such as name servers and distributed
- object management systems, through extension of its request methods.
- A feature of HTTP is the typing and negotiation of data
- representation, allowing systems to be built independently of the
- data being transferred.
-
- HTTP has been in use by the World-Wide Web global information
- initiative since 1990. This specification defines the protocol
- referred to as "HTTP/1.1".
-
-'
->>>
-```
-
-## Fechas y horas: `datetime`
-
-`datetime`, que ya ha aparecido anteriormente, es un módulo para gestión de
-fecha y hora. `datetime` soporta gran cantidad de operaciones y tipos de dato.
-Entre ellos los `timedeltas`, diferencias temporales que te permiten sumar y
-restar fechas con facilidad con los operadores habituales.
-
-Con las facilidades de `datetime`, es poco probable que necesites importar una
-librería de gestión de fecha y hora independiente.
-
-## Procesamiento de ficheros: `json` y `sqlite3`
-
-Python habilita gran cantidad de procesadores de formatos de fichero, los dos
-que se lista en este apartado tienen especial interés.
-
-El primer módulo, `json`, sirve para manipular datos en formato JSON. Sus dos
-funciones principales `dumps` y `loads` vuelcan o cargan datos de JSON a
-diccionarios o listas en una sola orden debido que la propia estructura de JSON
-está formada por parejas clave valor o listas de valores ordenadas por índices.
-Esta es la razón por la que en muchas ocasiones se utilizan ficheros de formato
-JSON para intercambiar información entre aplicaciones de python: su lectura y
-escritura es extremadamente sencilla.
-
-El segundo módulo, `sqlite3`, facilita el acceso a ficheros en formato SQLite3,
-un formato binario con una interfaz de acceso que permite consultas SQL. El
-módulo `sqlite3` es capaz de convertir las tablas que SQLite3 retorna a
-estructuras de python de forma transparente y cómoda por lo que es un aliado
-interesante para aplicaciones que requieren una base de datos pequeña y
-resiliente.
-
-## Aritmética de coma flotante decimal: `decimal`
-
-En el apartado sobre datos tratamos la complejidad de los números de coma
-flotante y que su representación binaria puede dar lugar a problemas. Este
-módulo de aritmética decimal aporta una solución rigurosa a este problema con
-el fin de facilitar el uso de python en entornos que requieren precisión
-estricta que incluso puede requerir cumplir con normativas, como puede ser la
-banca.
-
-La documentación del módulo muestra un par de ejemplos muy interesantes usando
-la clase `Decimal` aportada por este. Se adjuntan a continuación para que los
-estudies y los disecciones en busca de las diferencias con el uso de los
-números de coma flotante normales:
-
-``` python
->>> from decimal import *
->>> round(Decimal('0.70') * Decimal('1.05'), 2)
-Decimal('0.74')
->>> round(.70 * 1.05, 2)
-0.73
-
->>> Decimal('1.00') % Decimal('.10')
-Decimal('0.00')
->>> 1.00 % 0.10
-0.09999999999999995
-
->>> sum([Decimal('0.1')]*10) == Decimal('1.0')
-True
->>> sum([0.1]*10) == 1.0
-False
-```
-
-## Lo que has aprendido
-
-Más que aprender, en este apartado has sobrevolado la librería estándar de
-python desde la distancia y te has parado a observar qué problemas comunes ya
-están resueltos en python.
-
-La realidad es que la librería estándar es mucho más extensa que lo listado en
-este apartado, pero aquí se han mencionado los módulos que más frecuentemente
-se suelen usar en entornos normales y algunos otros que tienen interés a la
-hora de afianzar conocimiento que has obtenido en capítulos previos, como es el
-caso del módulo `decimal`.
-
-Este capítulo empieza a mostrarte el interés de programar en python y las
-posibilidades que tienes con él, únicamente con la librería estándar. En el
-próximo verás la cantidad de funcionalidad adicional que le puedes añadir con
-un par de librerías escritas por terceros.
-
-Sirva también este capítulo como recordatorio de lo extensa que es la librería
-estándar de python. En el futuro, cuando tengas intención de buscar una
-librería para resolver un problema que te despista de trabajar en la
-funcionalidad principal de tu aplicación, empieza por la librería estándar.
diff --git a/src/09_extralib.md b/src/09_extralib.md
deleted file mode 100644
index 65ed418..0000000
--- a/src/09_extralib.md
+++ /dev/null
@@ -1,162 +0,0 @@
-# Librerías útiles
-
-Ahora que ya sabes cómo instalar librerías y que has visto que muchas
-funcionalidades están contenidas en la librería estándar de python es un buen
-momento para que visites varios proyectos que aportan recursos muy interesantes
-a la hora de resolver problemas. Debido al carácter de uso general de python,
-estas librerías aportan facilidades muy diversas. El criterio para escogerlas
-parte de la experiencia personal del autor de este documento y añade algunas
-librerías y herramientas que pueden ser interesantes debido a su amplio uso en
-la industria.
-
-## Librerías científicas: ecosistema SciPy
-
-SciPy es un ecosistema de librerías de cálculo que tiene como objetivo
-facilitar la tarea de ingenieros, científicos y matemáticos en sus
-respectivos trabajos. Puedes considerarlo como un **Matlab** para python
-separado por componentes independientes.
-
-Además de ser el nombre del ecosistema, comparte nombre con una de las
-librerías fundamentales de éste. El ecosistema está formado por varias
-librerías, entre ellas se encuentran:
-
-- Numpy: un paquete de computación científica para python. Soporta matrices
- multidimensionales, funciones sofisticadas y álgebra lineal, entre otras.
-
-- SciPy: librería basada en Numpy que aporta rutinas numéricas eficientes para
- interpolación, optimización, algebra lineal, estadística y otros.
-
-- SymPy: una librería de matemática simbólica para python que complementa el
- resto de librerías del ecosistema, ya que casi todas están orientadas al
- análisis numérico.
-
-- Matplotlib: librería para representar gráficos y figuras científicas en 2D.
-
-- Pandas: aporta unas estructuras de datos muy potentes basadas en tablas, su
- objetivo es reforzar a python a la hora de tratar datos. El área de esta
- librería es el del análisis de datos, pero puede combinarse con otras áreas
- de estudio como la econometría (ver el proyecto statsmodels). Esta librería
- dispone de un ecosistema muy poderoso a su alrededor debido al auge del
- análisis de datos en la industria del software. Muchas librerías comparten la
- interfaz de Pandas por lo que tener nociones de su comportamiento abre muchas
- puertas en el sector del análisis de datos. Al igual que ocurre en SciPy, Las
- estructuras de datos de Pandas también se basan en Numpy.
-
-- IPython: más que una librería, IPython es una herramienta. Se trata de una
- REPL interactiva (su propio nombre viene de *Interactive Python*) que añade
- diversas funcionalidades sobre la REPL de python habitual: histórico de
- comandos, sugerencias, comandos mágicos (*magic*) que permiten alterar el
- comportamiento de la propia interfaz y un larguísimo etcétera. IPython,
- además, sirve como núcleo de los cuadernos Jupyter que integran una shell de
- python con visualización de tablas y gráficos para generar documentos estilo
- *literate programming*.
-
-## Machine Learning: ScikitLearn
-
-ScikitLearn es una librería de machine learning muy potente, implementa gran
-cantidad de algoritmos y tiene una documentación extensa, sencilla y educativa.
-
-Encaja a la perfección con el ecosistema SciPy, ya que se basa en NumPy, SciPy
-y Matplotlib.
-
-## Peticiones web: Requests
-
-Requests es una librería alternativa al módulo `urllib` aportado por la
-librería estándar de python. Se describe a sí misma como «HTTP para seres
-humanos», sugiriendo que `urllib` no es cómoda de usar.
-
-Requests gestiona automáticamente las URL-s de las peticiones a partir de los
-datos que se le entreguen, sigue redirecciones, almacena cookies, descomprime
-automáticamente, decodifica las respuestas de forma automática, etc. En general
-es una ayuda cuando no se quiere dedicar tiempo a controlar cada detalle de la
-conexión de modo manual.
-
-## Manipulación de HTML: Beautifulsoup
-
-Beautifulsoup es una librería de procesado de HTML extremadamente potente.
-Simplifica el acceso a campos de ficheros HTML con una sintaxis similar a los
-objetos de python, permitiendo acceder a la estructura anidada mediante el uso
-del punto en lugar de tener que lidiar con consultas extrañas o expresiones
-regulares. Esta funcionalidad puede combinarse con selectores CSS4 para un
-acceso mucho más cómodo.
-
-Muy usada en conjunción con Requests, para el desarrollo de aplicaciones de
-web-scraping. Aunque, si se desea usar algo más avanzado, se recomienda usar el
-framework Scrapy.
-
-## Tratamiento de imagen: Pillow
-
-Pillow es un fork de la librería PIL (*Python Imaging Library*) cuyo desarrollo
-fue abandonado. En sus diversos submódulos, Pillow permite acceder a datos de
-imágenes, aplicarles transformaciones y filtros, dibujar sobre ellas, cambiar
-su formato, etc.
-
-## Desarrollo web: Django, Flask
-
-Existen infinidad de frameworks y herramientas de desarrollo web en python. Así
-como en el caso del análisis de datos la industria ha convergido en una
-herramienta principal, Pandas, en el caso del desarrollo web hay muchas
-opciones donde elegir.
-
-Los dos frameworks web más usados son Django y Flask, siendo el segundo menos
-común que el primero pero digno de mención en conjunción con el otro por varias
-razones.
-
-La primera es la diferencia en la filosofía: así como Django decide con qué
-herramientas se debe trabajar, Flask, que se define a sí mismo como
-microframework, deja en manos de quien lo usa la elección de qué herramientas
-desea aplicarle. Cada una de las dos filosofías tiene ventajas y desventajas y
-es tu responsabilidad elegir las que más te convengan para tu proyecto.
-
-La segunda razón para mencionar Flask es que su código fuente es uno de los más
-interesantes a la hora de usar como referencia de cómo se debe programar en
-python. Define su propia norma de estilo de programación, basada en la
-sugerencia de estilo de python[^pep8] y su desarrollo es extremadamente
-elegante.
-
-Django, por su parte, ha sido muy influyente y muchas de sus decisiones de
-diseño han sido adoptadas por otros frameworks, tanto en python como en otros
-lenguajes. Lo que sugiere que está extremadamente bien diseñado.
-
-A pesar de las diferencias filosóficas, existen muchas similitudes entre ambos
-proyectos por lo que aprender a usar uno de ellos facilita mucho el uso del
-otro y no es aprendizaje perdido. No tengas miedo en lanzarte a uno.
-
-[^pep8]: <https://www.python.org/dev/peps/pep-0008/>
-
-
-## Protocolos de red: Twisted
-
-Twisted es motor de red asíncrono para python. Sobre él se han escrito
-diferentes librerías para gestión de protocolos de Internet como DNS, via
-Twisted-Names, IMAP y POP3, via Twisted-Mail, HTTP, via Twisted-Web, IRC y
-XMPP, via Twisted-Words, etc.
-
-El diseño asíncrono del motor facilita sobremanera las comunicaciones
-eficientes. Programar código asíncrono en python es relativamente sencillo,
-pero ha preferido dejarse fuera de este documento por diversas razones. Te
-animo a indagar en esta libreria para valorar el interés del código asíncrono.
-
-## Interfaces gráficas: PyQt, PyGTK, wxPython, PySimpleGUI
-
-A pesar de que python dispone de un módulo en su librería estándar para tratar
-interfaces gráficas llamado TKinter, es recomendable utilizar librerías más
-avanzadas para esto. TKinter es una interfaz a la herramienta Tk del lenguaje
-de programación Tcl y acompaña a python desde hace años.
-
-En programas simples TKinter es más que suficiente (IDLE, por ejemplo, está
-desarrollado con TKinter) pero a medida que se necesita complejidad o capacidad
-del usuario para configurar detalles de su sistema suele quedarse pequeño.
-
-Para programas complejos se recomienda usar otro tipo de librerías más
-avanzadas como PyQt, PyGTK o wxPython, todas ellas interfaces a librerías
-escritas en C/C++ llamadas Qt, GTK y wxWidgets respectivamente. Estas librerías
-aportan una visualización más elegante, en algunos casos usando widgets nativos
-del sistema operativo en el que funcionan.
-
-Debido a la complejidad del ecosistema nace el proyecto PySimpleGUI, que
-pretende aunar las diferentes herramientas en una sola, sirviendo de interfaz a
-cualquiera de las anteriores y alguna otra. Además, el proyecto aporta gran
-cantidad de ejemplos de uso. PySimpleGUI aún está en desarrollo y el soporte de
-algunos de los motores no está terminado, pero es una fuente interesante de
-información y recursos.
diff --git a/src/10_closing_words.md b/src/10_closing_words.md
deleted file mode 100644
index 404a58d..0000000
--- a/src/10_closing_words.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# Lo que has aprendido
-
-Rescatando la definición de la introducción:
-
-> Python es un lenguaje de programación de alto nivel orientado al uso general.
-> Fue creado por Guido Van Rossum y publicado en 1991. La filosofía de python
-> hace hincapié en la limpieza y la legibilidad del código fuente con una
-> sintaxis que facilita expresar conceptos en menos líneas de código que en
-> otros lenguajes.
->
-> Python es un lenguaje de tipado dinámico y gestión de memoria automática.
-> Soporta múltiples paradigmas de programación, incluyendo la programación
-> orientada a objetos, imperativa, funcional y procedural e incluye una extensa
-> librería estándar.
-
-Ahora sí que estás en condición de entenderla no sólo para python sino para
-cualquier otro lenguaje que se te presente de este modo. Ahora tienes la
-habilidad de poder comprender de un vistazo qué te aporta el lenguaje que
-tienes delante únicamente leyendo su descripción.
-
-Desgranándola poco a poco, has conocido la sintaxis de python en bastante
-detalle y has visto cómo hace uso de las sangrías para delimitar bloques de
-código, cosa que otros lenguajes hacen con llaves (`{}`) u otros símbolos.
-
-La facilidad de expresar conceptos complejos en pocas líneas de código puede
-verse en las *list comprehensions*, la sentencia `with` y muchas otras
-estructuras del sistema. Python es un lenguaje elegante y directo, similar al
-lenguaje natural.
-
-El tipado dinámico trata lo que estudiaste en el apartado sobre datos, donde se
-te cuenta que las referencias pueden cambiar de tipo en cualquier momento ya
-que son los propios valores los que son capaces de recordar qué tipo tienen.
-
-La gestión de memoria automática también se presenta en el mismo apartado,
-contándote que python hace uso de un *garbage collector* o recolector de basura
-para limpiar de la memoria los datos que ya no usa.
-
-Los diferentes paradigmas de programación no se han tratado de forma explícita
-en este documento, más allá de la programación orientada a objetos, que inunda
-python por completo. Sin embargo, el apartado sobre funciones adelanta varios
-de los conceptos básicos del paradigma de programación funcional: que las
-funciones sean ciudadanos de primera clase (*first-class citizens*), el uso de
-funciones anónimas (*lambda*) y las *closures*.
-
-Los paradigmas procedural e imperativo son la base para los dos
-paradigmas de los que hemos hablado. La programación imperativa implica que se
-programa mediante órdenes (el caso de python, recuerda) en lugar de
-declaraciones (como puede ser la programación lógica, donde se muestran un
-conjunto de normas que el programa debe cumplir). La programación procedural es
-un paradigma cuyo fundamento es el uso de bloques de código y su *scope*,
-creando funciones, estructuras de datos y variables aunque, a diferencia de la
-programación funcional, en la programación procedural no es necesario que las
-funciones sean ciudadanos de primera clase y pueden tener restricciones.
-
-Estos dos últimos paradigmas, en realidad, se soportan casi por accidente al
-habilitar los dos anteriores.
-
-En muchas ocasiones, te encontrarás escribiendo pequeñas herramientas y no
-necesitarás mucho más que usar las estructuras básicas de python y varias
-funciones para alterarlas, por lo que estarás pensando de forma procedural
-accidentalmente.
-
-Los paradigmas no son más que patrones de diseño que nos permiten clasificar
-los lenguajes y sus filosofías, pero son muy interesantes a la hora de diseñar
-nuestras aplicaciones.
-
-Además de todo esto, has tenido ocasión de conocer de forma superficial la
-librería estándar del lenguaje y un conjunto de librerías adicionales que te
-aportan los puntos de los que la librería estándar carece. Ahora sabes instalar
-dependencias y usarlas en entornos virtuales (*virtual environments*) para
-mantener limpia tu instalación.
-
-A parte de lo mencionado en la definición del lenguaje, has aprendido a
-ejecutar, cargar y distribuir módulos de python, algo primordial si pretendes
-crear paquetes o nuevas librerías y usar las de terceros.
-
-Con todo esto, tienes una visión general pero bastante detallada a nivel
-técnico de lo que python aporta y cómo. Lo que necesitas para compensarla es
-trabajar con él, acostumbrarte a su ecosistema y leer mucho código de buena
-calidad para acostumbrarte a seguir las convenciones y recetas habituales.
-
-
-## El código pythónico
-
-A lo largo del documento se tratan temas que puede que no te esperases
-encontrar al leer sobre programación, ya que tu interés principal es resolver
-tus problemas de forma efectiva y construir aplicaciones. Hacer robots que te
-hagan la vida más fácil, en definitiva.
-
-Sin embargo, quien se dedica a la programación tiene una vida muy ligada a la
-vida de quien se dedica a la filosofía o al diseño y es por eso que esas dos
-disciplinas aparecen de vez en cuando en cualquier conversación un poco seria
-sobre el trabajo con software.
-
-Las tres disciplinas, en primer lugar, ocurren en la mente de las personas y no
-en sus manos. Es por eso que los patrones mentales y los modos de pensamiento
-son parte fundamental de ellas. Ninguno de ellos son trabajos para los que se
-pueda entrenar una memoria muscular. Es necesario pensar. Y es necesario pensar
-de forma consciente y premeditada.
-
-Ver cómo desarrollan otras personas su actividad es valioso para realizar tu
-tarea con elegancia.
-
-Otro detalle que has debido de observar, sobre todo porque acaba de aparecer,
-es la *elegancia*. La elegancia es, hasta cierto punto, subjetiva y depende del
-gusto de quien la mira. Sin embargo, esto sólo es así hasta cierto punto, la
-realidad es que alguien puede considerar algo elegante y aun así no gustarle.
-Python es un ejemplo de algo así. Guste o no guste, python es un lenguaje de
-programación elegante, cuya elegancia forma parte primordial de la filosofía
-del lenguaje.
-
-El autor de este documento, por ejemplo, no es un entusiasta de python, pero a
-lo largo de la travesía de escribir este documento ha podido reencontrarse, una
-vez más, con su elegancia.
-
-El concepto del *código pythónico* (*pythonic code*) es un resultado de esto.
-Cuando se habla de código pythónico, se habla de un código que sigue los
-estándares de elegancia de python. Que es bonito, comprensible y claro. Un
-código que la comunidad de desarrollo de python aprobaría.
-
-Cuando programes en python, trata de programar código pythónico, pues es esa la
-verdadera razón por la que se creó el lenguaje y es la forma en la que el
-lenguaje más fácil te lo va a poner.
diff --git a/src/A_devtools.md b/src/A_devtools.md
deleted file mode 100644
index e856373..0000000
--- a/src/A_devtools.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Anexo I: Herramientas de desarrollo {-}
-
-IDLE es una herramienta de desarrollo muy limitada, suficiente para seguir los
-ejemplos que se recogen en este documento pero insuficiente para desarrollar
-aplicaciones avanzadas.
-
-## Desarrollo de código fuente
-
-Existen gran cantidad de entornos de desarrollo (IDE) avanzados y editores que
-pueden ser recomendables, aunque es cuestión de gusto personal decantarse por
-uno de ellos o por otro.
-
-La diferencia entre un entorno de desarrollo integrado y un editor es la
-siguiente: los entornos de desarrollo cumplen varias funciones adicionales,
-como en el caso de IDLE, dar acceso a una REPL de python y la posibilidad de
-analizar las variables en memoria. Los editores únicamente sirven para escribir
-el código, aunque en muchos casos la línea que separa ambos conceptos es
-bastante borrosa: existen editores con funcionalidades avanzadas y entornos
-integrados muy sencillos que parecen un simple editor. Resumiendo, los entornos
-integrados de desarrollo (IDE *integrated development environment*) tienen
-editores entre sus herramientas.
-
-### Entornos de desarrollo integrados
-
-Quien no utiliza entornos de desarrollo avanzados (una cuestión de gusto
-personal), por lo que se le hace difícil recomendar alguno en particular. Sin
-embargo, el wiki de python recoge una larguísima lista de editores y entornos
-de desarrollo integrado interesantes[^ides].
-
-[^ides]: <https://wiki.python.org/moin/PythonEditors>
-
-En el entorno del análisis de datos, la distribución *Anaconda* es muy usada.
-Anaconda es más que un entorno de desarrollo integrado. Incluye un entorno de
-desarrollo llamado Spyder, una shell propia, un gestor de paquetes y
-dependencias propio llamado Conda, posibilidad de integración con el lenguaje
-de programación R y gran cantidad de paquetes instalados por defecto.
-
-En otros entornos PyCharm es bastante común, aunque también son muy comunes los
-entornos de desarrollo integrados pensados para otros lenguajes que han
-comenzado a soportar python posteriormente como KDevelop, NetBeans y otros.
-
-Te recomiendo que, si usas un entorno de desarrollo integrado en otros
-lenguajes, investigues si soporta python. De este modo no tendrás que aprender
-una nueva herramienta. Al ser un lenguaje tan común, probablemente lo soporte.
-Si no lo soporta prueba con un IDE que siga una filosofía similar al que uses.
-
-### Editores de código
-
-Quien te escribe usa Vim, un editor de texto muy antiguo con muchas
-características que le hacen ser un editor muy eficiente. Existe gran variedad
-de editores de código que recomendar: Emacs, gEdit, Kate, Sublime, Atom... Todo
-dependerá de tus gustos personales.
-
-La ventaja principal de los editores de código es que conociendo uno en
-profundidad es más que suficiente para cualquier lenguaje, ya que están
-diseñados únicamente para escribir el contenido de tus programas, dejando las
-peculiaridades de ejecución de cada lenguaje a parte. Esto les aporta una
-ligereza difícilmente alcanzable por los IDEs.
-
-Sin embargo, esta virtud también es su mayor defecto. Al no integrar ninguna
-herramienta adicional, es necesario trabajar todo manualmente. En el caso de
-python, te fuerzan a usar una shell independiente y a interactuar con ella de
-forma manual. Al principio puede ser tedioso, pero aprender a gestionar los
-detalles manualmente es interesante ya que te permite obtener un gran
-conocimiento del sistema y lenguaje en el que trabajas.
-
-## Herramientas de depuración
-
-El propio diseño de python permite que sea fácilmente depurable. La REPL
-facilita que se pruebe la aplicación a medida que se va desarrollando, función
-a función, para asegurar que su comportamiento es el correcto. Además, la
-capacidad introspectiva del lenguaje es una buena herramienta, en conjunción
-con la REPL para comprobar el comportamiento.
-
-Además de estas características, la instalación de python viene acompañada del
-programa `pdb` (*Python Debugger*), un depurador de código similar al conocido
-[GNU-Debugger](https://en.wikipedia.org/wiki/GNU_Debugger). Existen otros
-depuradores de código más amigables que este, pero la realidad es que no suelen
-ser necesarios.
-
-## Testeo de aplicaciones
-
-Hoy en día el testeo de software es muy común, en parte gracias al desarrollo
-guiado por pruebas, o TDD (*Test Driven Development*).
-
-La librería estándar de python incluye un módulo para pruebas unitarias llamado
-`unittest` en que la librería Nose, muy conocida y usada, se basa para
-facilitar el trabajo de modo similar a lo que ocurre con Requests y `urllib`.
-
-Por supuesto, existen otras alternativas, pero estas son las principales.
diff --git a/src/Z_license.md b/src/Z_license.md
deleted file mode 100644
index 46d9792..0000000
--- a/src/Z_license.md
+++ /dev/null
@@ -1,337 +0,0 @@
-# Anexo II: Licencia CC BY-SA 4.0 {- #licencia-appendix}
-
-#### Creative Commons Atribución/Reconocimiento-CompartirIgual 4.0 Licencia Pública Internacional
-
-Al ejercer los Derechos Licenciados (definidos a continuación), Usted acepta y
-acuerda estar obligado por los términos y condiciones de esta Licencia
-Internacional Pública de Atribución/Reconocimiento-CompartirIgual 4.0 de
-Creative Commons ("Licencia Pública"). En la medida en que esta Licencia
-Pública pueda ser interpretada como un contrato, a Usted se le otorgan los
-Derechos Licenciados en consideración a su aceptación de estos términos y
-condiciones, y el Licenciante le concede a Usted tales derechos en
-consideración a los beneficios que el Licenciante recibe por poner a
-disposición el Material Licenciado bajo estos términos y condiciones.
-
-##### Sección 1 – Definiciones.
-
-a. **Material Adaptado** es aquel material protegido por Derechos de
- Autor y Derechos Similares que se deriva o se crea en base al
- Material Licenciado y en el cual el Material Licenciado se traduce,
- altera, arregla, transforma o modifica de manera tal que dicho
- resultado sea de aquellos que requieran autorización de acuerdo con
- los Derechos de Autor y Derechos Similares que ostenta el
- Licenciante. A los efectos de esta Licencia Pública, cuando el
- Material Licenciado se trate de una obra musical, una interpretación
- o una grabación sonora, la sincronización temporal de este material
- con una imagen en movimiento siempre producirá Material Adaptado.
-
-b. **Licencia de adaptador** es aquella licencia que Usted aplica a Sus
- Derechos de Autor y Derechos Similares en Sus contribuciones
- consideradas como Material Adaptado de acuerdo con los términos y
- condiciones de esta Licencia Pública.
-
-c. **Una Licencia Compatible con BY-SA** es aquella que aparece en la
- lista disponible en
- [creativecommons.org/compatiblelicenses](//creativecommons.org/compatiblelicenses),
- aprobada por Creative Commons, como una licencia esencialmente
- equivalente a esta Licencia Pública.
-d. **Derechos de Autor y Derechos Similares** son todos aquellos
- derechos estrechamente vinculados a los derechos de autor,
- incluidos, de manera enunciativa y no taxativa, los derechos sobre
- las interpretaciones, las emisiones, las grabaciones sonoras y los
- Derechos "Sui Generis" sobre Bases de Datos, sin importar cómo estos
- derechos se encuentren enunciados o categorizados. A los efectos de
- esta Licencia Pública, los derechos especificados en las secciones
- [2(b)(1)-(2)](#s2b) no se consideran Derechos de Autor y Derechos
- Similares.
-e. **Medidas Tecnológicas Efectivas** son aquellas medidas que, en
- ausencia de la debida autorización, no pueden ser eludidas en virtud
- de las leyes que cumplen las obligaciones del artículo 11 del
- Tratado de la OMPI sobre Derecho de Autor adoptado el 20 de
- diciembre de 1996, y/o acuerdos internacionales similares.
-f. **Excepciones y Limitaciones** son el uso justo (fair use), el trato
- justo (fair dealing) y/o cualquier otra excepción o limitación a los
- Derechos de Autor y Derechos Similares que se apliquen al uso el
- Material Licenciado.
-g. **Elementos de la Licencia** son los atributos que figuran en el
- nombre de la Licencia Pública de Creative Commons. Los Elementos de
- la Licencia de esta Licencia Pública son Atribución/Reconocimiento y
- CompartirIgual.
-h. **Material Licenciado** es obra artística o literaria, base de datos
- o cualquier otro material al cual el Licenciante aplicó esta
- Licencia Pública.
-i. **Derechos Licenciados** son derechos otorgados a Usted bajo los
- términos y condiciones de esta Licencia Pública, los cuales se
- limitan a todos los Derechos de Autor y Derechos Similares que
- apliquen al uso del Material Licenciado y que el Licenciante tiene
- potestad legal para licenciar.
-j. **Licenciante** es el individuo(s) o la entidad(es) que concede
- derechos bajo esta Licencia Pública.
-k. **Compartir** significa proporcionar material al público por
- cualquier medio o procedimiento que requiera permiso conforme a los
- Derechos Licenciados, tales como la reproducción, exhibición
- pública, presentación pública, distribución, difusión, comunicación
- o importación, así como también su puesta a disposición, incluyendo
- formas en que el público pueda acceder al material desde un lugar y
- momento elegido individualmente por ellos.
-l. **Derechos "Sui Generis" sobre Bases de Datos** son aquellos
- derechos diferentes a los derechos de autor, resultantes de la
- Directiva 96/9/EC del Parlamento Europeo y del Consejo, de 11 de
- marzo de 1996 sobre la protección jurídica de las bases de datos, en
- sus versiones modificadas y/o posteriores, así como otros derechos
- esencialmente equivalentes en cualquier otra parte del mundo.
-m. **Usted** es el individuo o la entidad que ejerce los Derechos
- Licenciados en esta Licencia Pública. La palabra **Su** tiene un
- significado equivalente.
-
-##### Sección 2 – Ámbito de Aplicación.
-
-a. **Otorgamiento de la licencia**.
- 1. Sujeto a los términos y condiciones de esta Licencia Pública, el
- Licenciante le otorga a Usted una licencia de carácter global,
- gratuita, no transferible a terceros, no exclusiva e irrevocable
- para ejercer los Derechos Licenciados sobre el Material
- Licenciado para:
- A. reproducir y Compartir el Material Licenciado, en su
- totalidad o en parte; y
- B. producir, reproducir y Compartir Material Adaptado.
-
- 2. [Excepciones y
- Limitaciones]{style="text-decoration: underline;"}. Para evitar
- cualquier duda, donde se apliquen Excepciones y Limitaciones al
- uso del Material Licenciado, esta Licencia Pública no será
- aplicable, y Usted no tendrá necesidad de cumplir con sus
- términos y condiciones.
- 3. [Vigencia]{style="text-decoration: underline;"}. La vigencia de
- esta Licencia Pública está especificada en la sección
- [6(a)](#s6a).
- 4. [Medios y formatos; modificaciones técnicas
- permitidas]{style="text-decoration: underline;"}. El Licenciante
- le autoriza a Usted a ejercer los Derechos Licenciados en todos
- los medios y formatos, actualmente conocidos o por crearse en el
- futuro, y a realizar las modificaciones técnicas necesarias para
- ello. El Licenciante renuncia y/o se compromete a no hacer valer
- cualquier derecho o potestad para prohibirle a Usted realizar
- las modificaciones técnicas necesarias para ejercer los Derechos
- Licenciados, incluyendo las modificaciones técnicas necesarias
- para eludir las Medidas Tecnológicas Efectivas. A los efectos de
- esta Licencia Pública, la mera realización de modificaciones
- autorizadas por esta sección [2(a)(4)](#s2a4) nunca produce
- Material Adaptado.
- 5. [Receptores posteriores]{style="text-decoration: underline;"}.
- A. [Oferta del Licenciante – Material
- Licenciado]{style="text-decoration: underline;"}. Cada
- receptor de Material Licenciado recibe automáticamente una
- oferta del Licenciante para ejercer los Derechos Licenciados
- bajo los términos y condiciones de esta Licencia Pública.
- B. [Oferta adicional por parte del Licenciante – Material
- Adaptado]{style="text-decoration: underline;"}. Cada
- receptor del Material Adaptado por Usted recibe
- automáticamente una oferta del Licenciante para ejercer los
- Derechos Licenciados en el Material Adaptado bajo las
- condiciones de la Licencia del Adaptador que Usted aplique.
- C. [Sin restricciones a receptores
- posteriores]{style="text-decoration: underline;"}. Usted no
- puede ofrecer o imponer ningún término ni condición
- diferente o adicional, ni puede aplicar ninguna Medida
- Tecnológica Efectiva al Material Licenciado si haciéndolo
- restringe el ejercicio de los Derechos Licenciados a
- cualquier receptor del Material Licenciado.
- 6. [Sin endoso]{style="text-decoration: underline;"}. Nada de lo
- contenido en esta Licencia Pública constituye o puede
- interpretarse como un permiso para afirmar o implicar que Usted,
- o que Su uso del Material Licenciado, está conectado,
- patrocinado, respaldado o reconocido con estatus oficial por el
- Licenciante u otros designados para recibir la
- Atribución/Reconocimiento según lo dispuesto en la sección
- [3(a)(1)(A)(i)](#s3a1Ai).
-
-b. **Otros derechos**.
-
- 1. Los derechos morales, tales como el derecho a la integridad, no
- están comprendidos bajo esta Licencia Pública ni tampoco los
- derechos de publicidad y privacidad ni otros derechos personales
- similares. Sin embargo, en la medida de lo posible, el
- Licenciante renuncia y/o se compromete a no hacer valer ninguno
- de estos derechos que ostenta como Licenciante, limitándose a lo
- necesario para que Usted pueda ejercer los Derechos Licenciados,
- pero no de otra manera.
- 2. Los derechos de patentes y marcas no son objeto de esta Licencia
- Pública.
- 3. En la medida de lo posible, el Licenciante renuncia al derecho
- de cobrarle regalías a Usted por el ejercicio de los Derechos
- Licenciados, ya sea directamente o a través de una entidad de
- gestión colectiva bajo cualquier esquema de licenciamiento
- voluntario, renunciable o no renunciable. En todos los demás
- casos, el Licenciante se reserva expresamente cualquier derecho
- de cobrar esas regalías.
-
-##### Sección 3 – Condiciones de la Licencia.
-
-Su ejercicio de los Derechos Licenciados está expresamente sujeto a las
-condiciones siguientes.
-
-a. **Atribución/Reconocimiento**.
-
- 1. Si Usted comparte el Material Licenciado (incluyendo en forma
- modificada), Usted debe:
-
- A. Conservar lo siguiente si es facilitado por el Licenciante
- con el Material Licenciado:
- i. identificación del creador o los creadores del Material
- Licenciado y de cualquier otra persona designada para
- recibir Atribución/Reconocimiento, de cualquier manera
- razonable solicitada por el Licenciante (incluyendo por
- seudónimo si este ha sido designado);
- ii. un aviso sobre derecho de autor;
- iii. un aviso que se refiera a esta Licencia Pública;
- iv. un aviso que se refiera a la limitación de garantías;
- v. un URI o un hipervínculo al Material Licenciado en la
- medida razonablemente posible;
-
- B. Indicar si Usted modificó el Material Licenciado y conservar
- una indicación de las modificaciones anteriores; e
- C. Indicar que el Material Licenciado está bajo esta Licencia
- Pública, e incluir el texto, el URI o el hipervínculo a esta
- Licencia Pública.
-
- 2. Usted puede satisfacer las condiciones de la sección
- [3(a)(1)](#s3a1) de cualquier forma razonable según el medio,
- las maneras y el contexto en los cuales Usted Comparta el
- Material Licenciado. Por ejemplo, puede ser razonable satisfacer
- las condiciones facilitando un URI o un hipervínculo a un
- recurso que incluya la información requerida.
- 3. Bajo requerimiento del Licenciante, Usted debe eliminar
- cualquier información requerida por la sección
- [3(a)(1)(A)](#s3a1A) en la medida razonablemente posible.
-
-b. **CompartirIgual**.
-
- Además de las condiciones de la sección [3(a)](#s3a), si Usted
- Comparte Material Adaptado producido por Usted, también aplican las
- condiciones siguientes.
-
- 1. La Licencia del Adaptador que Usted aplique debe ser una
- licencia de Creative Commons con los mismos Elementos de la
- Licencia, ya sea de esta versión o una posterior, o una Licencia
- Compatible con la BY-SA.
- 2. Usted debe incluir el texto, el URI o el hipervínculo a la
- Licencia del Adaptador que aplique. Usted puede satisfacer esta
- condición de cualquier forma razonable según el medio, las
- maneras y el contexto en los cuales Usted Comparta el Material
- Adaptado.
- 3. Usted no puede ofrecer o imponer ningún término o condición
- adicional o diferente, o aplicar ninguna Medida Tecnológica
- Efectiva al Material Adaptado que restrinja el ejercicio de los
- derechos concedidos en virtud de la Licencia de Adaptador que
- Usted aplique.
-
-##### Sección 4 – Derechos "Sui Generis" sobre Bases de Datos.
-
-Cuando los Derechos Licenciados incluyan Derechos "Sui Generis" sobre
-Bases de Datos que apliquen a Su uso del Material Licenciado:
-
-a. para evitar cualquier duda, la sección [2(a)(1)](#s2a1) le concede a
- Usted el derecho a extraer, reutilizar, reproducir y Compartir todo
- o una parte sustancial de los contenidos de la base de datos;
-b. si Usted incluye la totalidad o una parte sustancial del contenido
- de una base de datos en otra sobre la cual Usted ostenta Derecho
- "Sui Generis" sobre Bases de Datos, entonces ella (pero no sus
- contenidos individuales) se entenderá como Material Adaptado para
- efectos de la sección [3(b)](#s3b); y
-c. Usted debe cumplir con las condiciones de la sección [3(a)](#s3a) si
- Usted Comparte la totalidad o una parte sustancial de los contenidos
- de la base de datos.
-
-Para evitar dudas, esta sección [4](#s4) complementa y no sustituye Sus
-obligaciones bajo esta Licencia Pública cuando los Derechos Licenciados
-incluyen otros Derechos de Autor y Derechos Similares.
-
-##### Sección 5 – Exención de Garantías y Limitación de Responsabilidad.
-
-a. **Salvo que el Licenciante se haya comprometido mediante un acuerdo
- por separado, en la medida de lo posible el Licenciante ofrece el
- Material Licenciado tal como es y tal como está disponible y no se
- hace responsable ni ofrece garantías de ningún tipo respecto al
- Material Licenciado, ya sea de manera expresa, implícita, legal u
- otra. Esto incluye, de manera no taxativa, las garantías de título,
- comerciabilidad, idoneidad para un propósito en particular, no
- infracción, ausencia de vicios ocultos u otros defectos, la
- exactitud, la presencia o la ausencia de errores, sean o no
- conocidos o detectables. Cuando no se permita, totalmente o en
- parte, la declaración de ausencia de garantías, a Usted puede no
- aplicársele esta exclusión.**
-b. **En la medida de lo posible, en ningún caso el Licenciante será
- responsable ante Usted por ninguna teoría legal (incluyendo, de
- manera no taxativa, la negligencia) o de otra manera por cualquier
- pérdida, coste, gasto o daño directo, especial, indirecto,
- incidental, consecuente, punitivo, ejemplar u otro que surja de esta
- Licencia Pública o del uso del Material Licenciado, incluso cuando
- el Licenciante haya sido advertido de la posibilidad de tales
- pérdidas, costes, gastos o daños. Cuando no se permita la limitación
- de responsabilidad, ya sea totalmente o en parte, a Usted puede no
- aplicársele esta limitación.**
-c. La renuncia de garantías y la limitación de responsabilidad
- descritas anteriormente deberán ser interpretadas, en la medida de
- lo posible, como lo más próximo a una exención y renuncia absoluta a
- todo tipo de responsabilidad.
-
-##### Sección 6 – Vigencia y Terminación.
-
-a. Esta Licencia Pública tiene una vigencia de aplicación igual al
- plazo de protección de los Derechos de Autor y Derechos Similares
- licenciados aquí. Sin embargo, si Usted incumple las condiciones de
- esta Licencia Pública, los derechos que se le conceden mediante esta
- Licencia Pública terminan automáticamente.
-b. En aquellos casos en que Su derecho a utilizar el Material
- Licenciado se haya terminado conforme a la sección [6(a)](#s6a),
- este será restablecido:
-
- 1. automáticamente a partir de la fecha en que la violación sea
- subsanada, siempre y cuando esta se subsane dentro de los 30
- días siguientes a partir de Su descubrimiento de la violación; o
- 2. tras el restablecimiento expreso por parte del Licenciante.
-
- Para evitar dudas, esta sección [6(b)](#s6b) no afecta ningún
- derecho que pueda tener el Licenciante a buscar resarcimiento por
- Sus violaciones de esta Licencia Pública.
-
-c. Para evitar dudas, el Licenciante también puede ofrecer el Material
- Licenciado bajo términos o condiciones diferentes, o dejar de
- distribuir el Material Licenciado en cualquier momento; sin embargo,
- hacer esto no pondrá fin a esta Licencia Pública.
-d. Las secciones [1](#s1), [5](#s5), [6](#s6), [7](#s7), y [8](#s8)
- permanecerán vigentes a la terminación de esta Licencia Pública.
-
-##### Sección 7 – Otros Términos y Condiciones.
-
-a. El Licenciante no estará obligado por ningún término o condición
- adicional o diferente que Usted le comunique a menos que se acuerde
- expresamente.
-b. Cualquier arreglo, convenio o acuerdo en relación con el Material
- Licenciado que no se indique en este documento se considera separado
- e independiente de los términos y condiciones de esta Licencia
- Pública.
-
-##### Sección 8 – Interpretación.
-
-a. Para evitar dudas, esta Licencia Pública no es ni deberá
- interpretarse como una reducción, limitación, restricción, o una
- imposición de condiciones al uso de Material Licenciado que
- legalmente pueda realizarse sin permiso del titular, más allá de lo
- contemplado en esta Licencia Pública.
-b. En la medida de lo posible, si alguna disposición de esta Licencia
- Pública se considera inaplicable, esta será automáticamente
- modificada en la medida mínima necesaria para hacerla aplicable. Si
- la disposición no puede ser reformada, deberá ser eliminada de esta
- Licencia Pública sin afectar la exigibilidad de los términos y
- condiciones restantes.
-c. No se podrá renunciar a ningún término o condición de esta Licencia
- Pública, ni se consentirá ningún incumplimiento, a menos que se
- acuerde expresamente con el Licenciante.
-d. Nada en esta Licencia Pública constituye ni puede ser interpretado
- como una limitación o una renuncia a los privilegios e inmunidades
- que aplican al Licenciante o a Usted, incluyendo aquellos surgidos a
- partir de procesos legales de cualquier jurisdicción o autoridad.
-
-