{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Validar nombre de usuarios\n", "\n", "El objetivo de este ejercicio es crear una función que valide un nombre de usuario para que cumpla las siguientes condiciones:\n", "\n", "- El nombre de usuario debe contener un mínimo de 6 caracteres y un máximo de 12.\n", "- El nombre de usuario debe ser alfanumérico." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "def validate_username(username):\n", " if len(username) < 6 or len(username) > 12:\n", " raise Exception(\"La longitud del nombre de usuario tiene que ser entre 6 y 12 carácteres.\")\n", " if not username.isalnum():\n", " raise Exception(\"El nombre de usuario ha de ser alfanumérico.\")\n", " return True\n", "\n", "try:\n", " validate_username(\"foo\")\n", "except Exception as exp:\n", " print(exp)\n", "\n", "try:\n", " validate_username(\"foo123?\")\n", "except Exception as exp:\n", " print(exp)\n", "\n", "\n", "validate_username(\"foobar\")\n", "validate_username(\"foobar1\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Búsqueda de elementos\n", "\n", "Escribe una función que tome una lista ordenada de números, de menor a mayor, y un número. La función devolverá un booleano que indique si el elemento está o no en la lista." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "def find(ordered_list, element_to_find):\n", " for element in ordered_list:\n", " if element == element_to_find:\n", " return True\n", " return False\n", "\n", "print(find([1, 2, 3, 4, 5], 3))\n", "print(find([1, 2, 3, 4, 5], 36))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Listas superpuestas\n", "\n", "Escribe una función que tome como parámetros dos listas y devuelva True si tienen al menos un miembro en común, y False en caso contrario." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "def superposition(x, y):\n", " return bool(set(x) & set(y))\n", "\n", "print(superposition([1, 2], [3, 4]))\n", "print(superposition([1], [2, 3, 4]))\n", "print(superposition([1, 2], [2, 3, 4]))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Distancia de Hamming\n", "\n", "El objetivo de este ejercicio es escribir una función que calcule la distancia de Hamming entre dos cadenas de la misma longitud. La distancia de Hamming es el número de carácteres diferentes entre dos cadenas." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "def hamming(s1, s2):\n", " assert len(s1) == len(s2), \"Las candeas tienen que tener la misma longitud\"\n", " return sum(1 for i in range(len(s1)) if s1[i] != s2[i])\n", "\n", "print(hamming(\"1011101\", \"1001001\"))\n", "print(hamming(\"2143896\", \"2233796\"))\n", "print(hamming(\"tener\", \"reses\"))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Eliminar duplicados\n", "\n", "Crea una función que dada una lista cualquiera, devuelva una copia de esta lista eliminando los elementos duplicados." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "def remove_duplicated(values):\n", " return list(set(values))\n", "\n", "values = [12, 24, 35, 24, 88, 120, 155, 88, 120, 155]\n", "unique_values = remove_duplicated(values)\n", "print(values)\n", "print(unique_values)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Palabras más frecuentes\n", "\n", "Descarga el fichero con el libro de \"La Isla del Tesoro\" desde [este enlace](https://gist.githubusercontent.com/marcosgabarda/266b73e454b0f9d4655646c801b074cb/raw/404ed7729025b395812241fd443aee6bcfe71bcb/Treasure_Island.txt). Lee el fichero y extrae las 30 palabras más frecuentes de todo el libro.\n", "\n", "### Ayuda\n", "\n", "Python incluye un módulo llamado `re` para el uso de expresiones regulares. Puedes investigar como funciona y usarlo para resolver este ejercicio." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "import re\n", "\n", "\n", "counter = {}\n", "\n", "with open(\"Treasure_Island.txt\", \"r\") as book:\n", " for line in book:\n", " words = re.split(r\"\\W\", line.lower())\n", " for word in words:\n", " if not word or len(word) <= 2:\n", " continue\n", " if word not in counter:\n", " counter[word] = 1\n", " else:\n", " counter[word] += 1\n", "counter = list(counter.items())\n", "counter.sort(key=lambda item: item[1], reverse=True)\n", "print(counter[:30])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Títulos de los capítulos\n", "\n", "Teniendo en cuenta que en el libro anterior, los cápitulos siempre están de la siguiente forma:\n", "\n", "```\n", "{numero del capítulo}\\n\n", "\\n\n", "{titulo del capítulo}```\n", "\n", "Muestra una lista con todos los títulos y números de los capítulos de \"La Isla del Tesoro\"." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "with open(\"Treasure_Island.txt\", \"r\") as book:\n", " for line in book:\n", " line = line.strip()\n", " if line.strip().isdigit():\n", " next(book)\n", " title = next(book).strip()\n", " print (\"{} - {}\".format(line, title))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Bandeja de entrada\n", "En este ejercicio vamos a crear una bandeja de entrada de mensajes enviados a usuarios, así como tres funciones, una para enviar mensajes, otra obtener los pendientes de leer y otra para leer un mensaje.\n", "\n", "- Cada mensaje tendrá tres campos, origen (nombre del usuarios que lo envió), contenido, y si se ha leído o no\n", "- Los usuarios se crean de forma dinámica al enviar un mensaje\n", "- La función para enviar mensajes recibe el nombre del usuario que lo envía y el contenido\n", "- La función para leer un mensaje muestra el contenido, quien lo envía lo marca como leído.\n", "- La función para obtener el listado de los mensajes pendientes por leer solo muestra los que no están leídos, un resumen del contenido del mensaje, y un identificador de este." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "# Base de datos para el inbox\n", "inbox = {}\n", "\n", "def send(user, content):\n", " \"\"\"Envia un mensaje.\"\"\"\n", " message = {\"origin\": user, \"content\": content, \"read\": False}\n", " if user not in inbox:\n", " inbox[user] = [message]\n", " else:\n", " inbox[user].append(message)\n", "\n", "def unread(user):\n", " user_inbox = inbox.get(user, [])\n", " unread_messages = [(index, message) for index, message in enumerate(user_inbox) if not message[\"read\"]]\n", " for unread_message in unread_messages:\n", " print('{}: \"{}...\" - {}'.format(\n", " unread_message[0], \n", " unread_message[1][\"content\"][:10],\n", " unread_message[1][\"origin\"],\n", " ))\n", "\n", "def show(user, index):\n", " user_inbox = inbox.get(user, [])\n", " try:\n", " message = user_inbox[index]\n", " except IndexError:\n", " print(\"Mensaje no encontrado.\")\n", " else:\n", " print('\"{}\"\\n- {}'.format(message[\"content\"], message[\"origin\"]))\n", " user_inbox[index][\"read\"] = True\n", "\n", "send(\"marcos\", \"Hola! esto es un texto muy largo\")\n", "send(\"marcos\", \"me lees? no se si me lees o no\")\n", "unread(\"marcos\")\n", "show(\"marcos\", 0)\n", "unread(\"marcos\")\n", "show(\"marcos\", 1)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Simulador de notificaciones\n", "\n", "El objetivo de este ejercicio es desarrollar un **simulador de entrega de notificaciones a usuarios**. El simulador permitirá definir un entorno con varios usuarios a los que se le pueden entregar notificaciones y cada una de estas notificaciones tendrá una serie de estadísticas relacionadadas con los mensajes enviados, los recibidos y los abiertos.\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "- Registrar usuarios.\n", "\n", " - Cada usario tendrá un nombre, que será usado para su representación.\n", " - Un usuario será registrado en un listado único y se le asignará un código.\n", " - Cada usuario tendrá una bandeja de entrada con los mensajes que ha recibido.\n", " - Cada usuario podrá comprobar su bandeja de entrada y marcar un mensaje como leído.\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "- Crear y enviar mensajes:\n", "\n", " - Los mensajes tendrán una cadena de texto como cuerpo, que será usada para su representación.\n", " - Los mensajes tendrán una lista de usuarios a los que se les ha enviado el mensaje.\n", " - Los mensajes tendrán una lista de usuarios a los que han recibido el mensaje.\n", " - Los mensajes tendrán una lista de usuarios a los que han abierto el mensaje.\n", " - Dado un mensaje, se podrán obtener las estadísticas de apertura y recepción del mensaje.\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "- El **sitema de entrega de mensajes** estará separado de los usuarios y los mensajes, y deberá:\n", "\n", " - Mantener un registro de los usuarios registrados, asignando sus códigos.\n", " - Publicar un mensaje a todos los usuarios registrados, asignando en ese proceso un código único a cada mensaje, y añdiendo acada mensaje en la bandeja de entrada del usuario.\n", " - Simular un ratio de pérdida de mensajes, es decir, que exista una probabilidad de que un mensaje no sea recibido por un usuario.\n", " - Además de las definiciones de las clases necesarias, habrá que implementar una simulación de envío de mensajes a 1000 usuarios y mostrar las estadísticas.\n", "\n", "Hay que tener en cuenta que cada uno de los usuarios a los que le llege el mensaje tendrá que poder obtener una lista de los mensajes que se le han enviado, sabiendo si lo ha leído o no.\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Ayuda\n", "\n", "Para crear identificadores únicos, podemos usar el módulo uuid de Python:\n", "\n", "```python\n", ">>> import uuid\n", ">>> str(uuid.uuid1())\n", "'9d6a768a-3659-11e8-966c-60f81db53974'\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "import uuid\n", "import random\n", "\n", " \n", "class Message:\n", " \n", " def __init__(self, body=None):\n", " self.body = body\n", " self.dispatcher = Dispatcher()\n", " \n", " # Estadísitcas\n", " self.sent_to = []\n", " self.receibed_by = []\n", " self.read_by = []\n", "\n", " def __str__(self):\n", " return self.body\n", " \n", " def send(self):\n", " self.code = self.dispatcher.publish(self)\n", "\n", " def stats(self):\n", " return {\n", " \"sent_to\": len(self.sent_to),\n", " \"receibed_by\": len(self.receibed_by),\n", " \"read_by\": len(self.read_by),\n", " }\n", "\n", "class UserMessage:\n", "\n", " def __init__(self, user, message):\n", " self.user = user\n", " self.message = message\n", " self.read = False\n", "\n", " def __str__(self):\n", " return str(self.message)\n", " \n", " @property\n", " def code(self):\n", " return self.message.code\n", " \n", " def mark_as_read(self):\n", " self.read = True\n", " self.message.read_by.append(self.user)\n", " \n", "class User:\n", " \n", " def __init__(self, name=None):\n", " self.dispatcher = Dispatcher()\n", " self.name = name\n", " self.inbox = []\n", "\n", " def __str__(self):\n", " return self.name\n", " \n", " def register(self):\n", " self.code = self.dispatcher.register(self)\n", "\n", " def read(self, code):\n", " for message in self.inbox:\n", " if message.code == code:\n", " message.mark_as_read()\n", "\n", "\n", "class Dispatcher:\n", " \n", " loss_ratio = 0.01 # Para la simulación, de media, el 1% de los mensajes se perderán\n", " users = {}\n", " \n", " def register(self, user):\n", " # Registra el usuario\n", " code = str(uuid.uuid4())\n", " self.users[code] = user\n", " return code\n", " \n", " def publish(self, message):\n", " code = str(uuid.uuid4())\n", " \n", " for user in self.users.values():\n", " \n", " # Añade user como receptor del mensaje\n", " message.sent_to.append(user)\n", " \n", " # Simular ratio de perdida\n", " loss = random.random() < self.loss_ratio\n", " if not loss:\n", " message.receibed_by.append(user)\n", " user_message = UserMessage(user, message)\n", " user.inbox.append(user_message)\n", "\n", " return code\n", "\n", "\n", "# Registrar 1000 usuarios\n", "users = [User(f\"user{i}\") for i in range(1000)]\n", "[user.register() for user in users]\n", "\n", "# Enviar un mensaje\n", "msg = Message(\"Hola!\")\n", "msg.send()\n", "\n", "# Ver estadísticas recién enviadas\n", "print(msg.stats())\n", "\n", "# Simular aperturas\n", "for user in random.sample(users, random.randint(0, len(users) - 1)):\n", " if user.inbox:\n", " last_message = user.inbox[-1]\n", " user.read(last_message.code)\n", "\n", "# Ver estadísticas con aperturas\n", "print(msg.stats())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Soporte para envíos individualizados en simulador de notificaciones\n", "\n", "En el escenario planteado anteriormente sólo se da soporte para simular envíos en broadcast. Añade una nueva clase de mensaje y modifica el servidor para permitir el envío de mensajes individualizados." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "mport uuid\n", "import random\n", "\n", " \n", "class Message:\n", " \n", " def __init__(self, body=None):\n", " self.body = body\n", " self.dispatcher = Dispatcher()\n", "\n", " # Estadísitcas\n", " self.sent_to = []\n", " self.receibed_by = []\n", " self.read_by = []\n", "\n", " def __str__(self):\n", " return self.body\n", " \n", " def send(self):\n", " self.code = self.dispatcher.publish(self)\n", "\n", " def stats(self):\n", " return {\n", " \"sent_to\": len(self.sent_to),\n", " \"receibed_by\": len(self.receibed_by),\n", " \"read_by\": len(self.read_by),\n", " }\n", "\n", "class SingleMessage(Message):\n", " \n", " def __init__(self, body=None):\n", " super().__init__(body=body)\n", " # Destinatarios\n", " self.users = []\n", "\n", " def recipients(self, users):\n", " self.users = users\n", "\n", " def send(self):\n", " self.code = self.dispatcher.publish(self, users=self.users)\n", "\n", "class UserMessage:\n", "\n", " def __init__(self, user, message):\n", " self.user = user\n", " self.message = message\n", " self.read = False\n", "\n", " def __str__(self):\n", " return str(self.message)\n", " \n", " @property\n", " def code(self):\n", " return self.message.code\n", " \n", " def mark_as_read(self):\n", " self.read = True\n", " self.message.read_by.append(self.user)\n", " \n", "class User:\n", " \n", " def __init__(self, name=None):\n", " self.dispatcher = Dispatcher()\n", "\n", " # Datos de usuario\n", " self.name = name\n", " self.inbox = []\n", "\n", " def __str__(self):\n", " return self.name\n", " \n", " def register(self):\n", " self.code = self.dispatcher.register(self)\n", "\n", " def read(self, code):\n", " for message in self.inbox:\n", " if message.code == code:\n", " message.mark_as_read()\n", "\n", "\n", "class Dispatcher:\n", " \n", " loss_ratio = 0.01 # Para la simulación, de media, el 1% de los mensajes se perderán\n", " users = {}\n", " \n", " def register(self, user):\n", " # Registra el usuario\n", " code = str(uuid.uuid4())\n", " self.users[code] = user\n", " return code\n", " \n", " def publish(self, message, users=None):\n", " code = str(uuid.uuid4())\n", " \n", " recipients = users or self.users.values()\n", " \n", " for user in recipients:\n", " \n", " # Añade user como receptor del mensaje\n", " message.sent_to.append(user)\n", " \n", " # Simular ratio de perdida\n", " loss = random.random() < self.loss_ratio\n", " if not loss:\n", " message.receibed_by.append(user)\n", " user_message = UserMessage(user, message)\n", " user.inbox.append(user_message)\n", "\n", " return code\n", "\n", "\n", "# Registrar 1000 usuarios\n", "users = [User(f\"user{i}\") for i in range(1000)]\n", "[user.register() for user in users]\n", "\n", "# Enviar un mensaje\n", "msg = Message(\"Hola!\")\n", "msg.send()\n", "\n", "# Ver estadísticas recién enviadas\n", "print(msg.stats())\n", "\n", "# Simular aperturas\n", "for user in random.sample(users, random.randint(0, len(users) - 1)):\n", " if user.inbox:\n", " last_message = user.inbox[-1]\n", " user.read(last_message.code)\n", "\n", "# Ver estadísticas con aperturas\n", "print(msg.stats())\n", "\n", "# Envio sencillo\n", "msg = SingleMessage(\"Hola user1!\")\n", "msg.recipients([users[0]])\n", "msg.send()\n", "\n", "# Ver estadísticas recién enviadas\n", "print(msg.stats())\n", "\n", "# Ver estadísticas con aperturas\n", "last_message = users[0].inbox[-1]\n", "users[0].read(last_message.code)\n", "\n", "# Ver estadísticas con aperturas\n", "print(msg.stats())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Módulos para el simulador de mensajes\n", "\n", "El objetivo de este ejercico es tomar el código que se ha desarrollado en el ejercicio anterior y escribirlo en diferentes módulos.\n", "\n", "Uno de los módulos deberá de permitir ser ejecutado como script para lanzar una simulación." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Decorador para medir tiempos\n", "\n", "Python tiene muchos módulos estándar muy útiles, uno de ellos es el módulo `time`, que proporciona funciones para la gestión del tiempo.\n", "\n", "La función más sencilla sería obtener el timestamp actual." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "import time\n", "\n", "time.time()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "El objetivo de este ejercicio es desarrollar un decorador que permita medir el tiempo que tarda en ejecutarse los métodos de una clase y que el resultado se pueda mostrar o por pantalla o pueda ser guardado en un fichero." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Voy a hacer cosas 6 segundos\n", "Time: 6.002283s\n" ] } ], "source": [ "import time\n", "import random\n", "\n", "\n", "def time_logger(output=None):\n", " def time_logger_decorator(function):\n", " def wrapper(self=None, *args, **kwargs):\n", " initial_time = time.time()\n", " result = function(self, *args, **kwargs)\n", " finish_time = time.time()\n", " record = \"Time: {:f}s\".format(finish_time - initial_time)\n", " if not output:\n", " print(record)\n", " else:\n", " with open(output, \"w\") as out:\n", " out.write(record)\n", " return result\n", " return wrapper\n", " return time_logger_decorator\n", "\n", "@time_logger()\n", "def dummy_test(seconds):\n", " sleep = random.randint(1, seconds)\n", " print(\"Voy a hacer cosas {} segundos\".format(sleep))\n", " time.sleep(sleep)\n", "\n", "\n", "dummy_test(10)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Fibonacci como un generador\n", "\n", "El objetivo de este ejercicio es reescribir la funcíon de Fibonacci para que genere de forma infinita todos los números de esta secuencia." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def fib(): \n", " \"\"\"Generador para Fibonacci.\"\"\"\n", " a, b = 0, 1\n", " while True:\n", " yield a\n", " a, b = b, a+b\n", "\n", "\n", "generator = fib()\n", "[ next(generator) for _ in range(10)]" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }