{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "eWR9iZ8qQeYM"
   },
   "source": [
    "<p>\n",
    "<font size='5' face='Georgia, Arial'>IIC2233 Apunte Programación Avanzada</font><br>\n",
    "<font size='1'>&copy; 2015 Karim Pichara - Christian Pieringer. Todos los derechos reservados.</font>\n",
    "<br>\n",
    "<font size='1'> Modificado desde 2017-2 al 2025-2 por Equipo Docente IIC2233</font>\n",
    "</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tabla de contenidos\n",
    "\n",
    "1. [Introducción](#Introducción)\n",
    "    1. [Clasificación de las estructuras de datos](#Clasificación-de-las-estructuras-de-datos)\n",
    "2. [Tuplas](#Tuplas)\n",
    "    1. [Desempaquetado de elementos](#Desempaquetado-de-elementos)\n",
    "    2. [*Slicing* de tuplas](#Slicing-de-tuplas)\n",
    "    3. [*Named tuples*](#Named-tuples)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "JpbSQ296QeYO"
   },
   "source": [
    "# Introducción\n",
    "En los modelos tradicionales de programación, estamos permanentemente manipulando datos. Es por esto que para facilitar esta manipulación, se han creado construcciones que permiten **agrupar y manipular** eficientemente conjuntos de datos.\n",
    "\n",
    "En Ciencia de la Computación, estas construcciones se conocen como **estructuras de datos** y consisten de una manera de agrupar datos relacionados, junto con un conjunto de **operaciones para accederlos y modificarlos de manera eficiente**. La mayoría de los lenguajes de programación incluyen soporte para algunas estructuras de datos predefinidas (*built-in*) y también permiten definir estructuras nuevas.\n",
    "\n",
    "A diferencia de las variables simples o \"primitivas\" como los enteros, o los reales, las estructuras de datos involucran un mayor nivel de *abstracción*. En esta semana estudiaremos el modelo conceptual de algunas estructuras de datos típicas utilizadas en ciencia de la computación, así como también su implementación en Python. \n",
    "\n",
    "La decisión de \"qué estructura de datos utilizar\" dependerá tanto del contexto de la aplicación en que se desea usar, como también de su diseño y *eficiencia* esperada. Al final de esta semana, habremos aprendido que la elección adecuada de una estructura de datos para cada situación es crucial para desarrollar un *software* eficiente.\n",
    "\n",
    "#### Iterar/Iterable\n",
    "Al programar, varias veces te encontrarás con la necesidad de recorrer una estructura. Al concepto de recorrer le llamamos \"iterar\", y a lo que puede ser recorrido le llamamos que es \"iterable\". Por ahora basta con reconocer las palabras y entender \"iterar\" como \"recorrer\", e \"iterable\" como \"que puede ser recorrido\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "S8o-99HuQeYW"
   },
   "source": [
    "## Clasificación de las estructuras de datos\n",
    "\n",
    "Dependiendo de sus propiedades, clasificaremos las estructuras de datos en dos grandes categorías; las estructuras de datos **secuenciales** y las estructuras de datos **no secuenciales**.\n",
    "\n",
    "#### Estructuras de datos secuenciales\n",
    "Corresponden a estructuras basadas en un **ordenamiento secuencial** de los elementos, que dependerá de cómo son ingresados en la estructura. Todas las estructuras de este tipo permiten recorrer los datos que contienen siguiendo el orden que induce la estructura.\n",
    "\n",
    "Caben dentro de esta clasificiación los *built-ins* `tuple`, `list`, y `str` y las estructuras de *stack* y *cola*.\n",
    "\n",
    "#### Estructuras de datos no secuenciales\n",
    "Por otra parte, las estructuras no secuenciales almacenan los datos **sin establecer un orden fijo** de acceso a ellos. Estas estructuras permiten recorrer todos los datos que contienen, pero no garantizan en qué orden se entregarán sus elementos o si este orden se mantendrá constante durante la ejecución del programa.\n",
    "\n",
    "A pesar de lo anterior, estas estructuras son ampliamante utilizadas, ya que que proveen métodos muy eficientes para la **búsqueda** de datos.\n",
    "\n",
    "Las dos estructuras no secuenciales que revisaremos durante el curso son los diccionarios y los conjuntos (*sets*). Pero no son las únicas, también existen los árboles, grafos y *heaps*."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "4YBLEUvbQeYX"
   },
   "source": [
    "# Tuplas\n",
    "\n",
    "Las [**tuplas** (`tuple`)](https://docs.python.org/3/library/stdtypes.html#tuple) se utilizan para manejar datos de forma **ordenada** e **inmutable**, es decir, no se pueden cambiar los valores que contiene. Para acceder a algún elemento de una tupla, es necesario usar índices correlativos al orden en que los valores fueron agregados.\n",
    "\n",
    "![](img/indices_secuencia.png)\n",
    "\n",
    "Las tuplas pueden ser heterogéneas, y de hecho es su uso más común, lo que significa que pueden contener objetos pertenecientes a clases o tipos de datos distintos, incluyendo otras tuplas. Una tupla se puede crear de las siguientes maneras:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 85,
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 706,
     "status": "ok",
     "timestamp": 1520879324770,
     "user": {
      "displayName": "Cristian Ruz",
      "photoUrl": "//lh4.googleusercontent.com/-0WhxkzATSqY/AAAAAAAAAAI/AAAAAAAAIqQ/WE7Rcv4pnN8/s50-c-k-no/photo.jpg",
      "userId": "108117710965209443485"
     },
     "user_tz": 180
    },
    "id": "N-rV68cLQeYY",
    "outputId": "726687d1-e857-4f59-d7ca-bd3d2b0e0f42"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'tuple'> ()\n",
      "<class 'tuple'> (0, 1, 2) 0 1\n",
      "<class 'tuple'> (0,)\n",
      "<class 'tuple'> (0, 'uno') 0 uno\n"
     ]
    }
   ],
   "source": [
    "# Usando tuple() sin ingresar elementos, se crea una tupla vacía.\n",
    "a = tuple()\n",
    "\n",
    "# Declarando explícitamente los elementos de la tupla,\n",
    "# ingresándolos entre paréntesis.\n",
    "b = (0, 1, 2)\n",
    "\n",
    "# Cuando creamos una tupla de tamaño 1, debemos incluir una coma al final.\n",
    "c = (0, )\n",
    "\n",
    "# Pueden ser creadas con objetos de distinto tipo.\n",
    "# Al momento de la creación se pueden omitir los paréntesis.\n",
    "d = 0, 'uno'\n",
    "\n",
    "print(type(a), a)\n",
    "print(type(b), b, b[0], b[1])\n",
    "print(type(c), c)\n",
    "print(type(d), d, d[0], d[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "CSWPkYTVQeYc"
   },
   "source": [
    "Las tuplas son estructuras de datos **inmutables**. Esto significa que **no es posible agregar o eliminar elementos**, o bien cambiar el contenido de la tupla una vez que ésta fue creada.\n",
    "\n",
    "En el siguiente ejemplo, la posición 2 de la tupla `a` contiene originalmente un `float`. Si intentamos reemplazar el contenido de esta posición por un *string* (o cualquier otro valor), se genera un *error de tipo* (`TypeError`), debido a que los objetos de la clase `tuple` *no permiten asignación*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "code",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 198,
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 621,
     "status": "error",
     "timestamp": 1520879327058,
     "user": {
      "displayName": "Cristian Ruz",
      "photoUrl": "//lh4.googleusercontent.com/-0WhxkzATSqY/AAAAAAAAAAI/AAAAAAAAIqQ/WE7Rcv4pnN8/s50-c-k-no/photo.jpg",
      "userId": "108117710965209443485"
     },
     "user_tz": 180
    },
    "id": "DaAmguADQeYd",
    "outputId": "2e4b3fac-f617-4428-fe76-26d32015632f"
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'tuple' object does not support item assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[2], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m a \u001b[38;5;241m=\u001b[39m (\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mChile\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m4.15\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAgosto\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m----> 2\u001b[0m \u001b[43ma\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124msemestre\u001b[39m\u001b[38;5;124m'\u001b[39m\n",
      "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment"
     ]
    }
   ],
   "source": [
    "a = ('Chile', 2, 4.15, 'Agosto')\n",
    "a[2] = 'semestre'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Y8r_x5yHdaGN"
   },
   "source": [
    "Sin embargo, sí es posible modificar algún valor contenido *dentro* de un elemento de la tupla, siempre que el tipo de datos lo permita. En el siguiente caso **no** estamos modificando el objeto `tuple`, sino un valor interno (la posición 0) de la lista que está en la posición 3 de la tupla `meses`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 34,
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 732,
     "status": "ok",
     "timestamp": 1520880114636,
     "user": {
      "displayName": "Cristian Ruz",
      "photoUrl": "//lh4.googleusercontent.com/-0WhxkzATSqY/AAAAAAAAAAI/AAAAAAAAIqQ/WE7Rcv4pnN8/s50-c-k-no/photo.jpg",
      "userId": "108117710965209443485"
     },
     "user_tz": 180
    },
    "id": "xgumMpoXd3If",
    "outputId": "815615ff-9bba-4878-fb9e-d575e2b6fe8d"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2023, 'semestre', 2, ['Ene', 'Sep', 'Oct', 'Nov', 'Dic'])\n"
     ]
    }
   ],
   "source": [
    "meses = (2023, \"semestre\", 2, ['Ago', 'Sep', 'Oct', 'Nov', 'Dic'])\n",
    "\n",
    "meses[3][0] = 'Ene'\n",
    "print(meses)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "QPtgAFHIQeYg"
   },
   "source": [
    "### Desempaquetado de elementos\n",
    "\n",
    "Las tuplas pueden ser **desempaquetadas** en variables individuales. En el siguiente ejemplo creamos una función llamada `calcular_geometria()`, que recibe como entrada los lados de un cuadrilátero y retorna algunas medidas geométricas. Cuando las funciones retornan más de un valor, lo hacen empaquetando todos los valores en una tupla. Esto es simplemente un [truco](https://en.wikipedia.org/wiki/Syntactic_sugar) de Python, replicable en otros lenguajes, para aparentar que se entregan múltiples valores de retorno."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 85,
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 653,
     "status": "ok",
     "timestamp": 1520880118274,
     "user": {
      "displayName": "Cristian Ruz",
      "photoUrl": "//lh4.googleusercontent.com/-0WhxkzATSqY/AAAAAAAAAAI/AAAAAAAAIqQ/WE7Rcv4pnN8/s50-c-k-no/photo.jpg",
      "userId": "108117710965209443485"
     },
     "user_tz": 180
    },
    "id": "wB7LtfXeQeYh",
    "outputId": "d18ef0f7-e486-4543-c9b7-e4dc75b2ac71"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1: (200.0, 60.0, 10.0, 5.0)\n",
      "<class 'tuple'>\n",
      "2: 60.0\n",
      "3: 200.0, 60.0, 10.0, 5.0\n",
      "4: 200.0, 60.0, 10.0, 5.0\n"
     ]
    }
   ],
   "source": [
    "from typing import Tuple\n",
    "\n",
    "\n",
    "def calcular_geometria(a: float, b: float) -> Tuple[float]:\n",
    "    area = a * b\n",
    "    perimetro = (2 * a) + (2 * b)\n",
    "    punto_medio_a = a / 2\n",
    "    punto_medio_b = b / 2\n",
    "    # Los paréntesis son opcionales, ya que estamos creando una tupla\n",
    "    return (area, perimetro, punto_medio_a, punto_medio_b)\n",
    "\n",
    "\n",
    "# Obtenemos una tupla con los datos provenientes de la función.\n",
    "data = calcular_geometria(20.0, 10.0)\n",
    "print(f\"1: {data}\")\n",
    "\n",
    "# El tipo de dato obtenido es 'tuple'\n",
    "print(type(data))\n",
    "\n",
    "# Obtenemos un valor desde la tupla directamente usando su índice\n",
    "p = data[1]\n",
    "print(f\"2: {p}\")\n",
    "\n",
    "# Desempaquetamos en variables independientes\n",
    "# los valores contenidos en una tupla\n",
    "a, p, mpa, mpb = data\n",
    "print(f\"3: {a}, {p}, {mpa}, {mpb}\")\n",
    "\n",
    "# Las funciones devuelven el conjunto de valores\n",
    "# como una tupla. Se puede desempaquetar directamente\n",
    "# en variables individuales como en el caso anterior.\n",
    "a, p, mpa, mpb = calcular_geometria(20.0, 10.0)\n",
    "print(f\"4: {a}, {p}, {mpa}, {mpb}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Operador de Desempaquetado \n",
    "\n",
    "Además de las formas mencionadas anteriormente para desempaquetar estas estructuras de datos, Python nos ofrece poder realizar lo mismo, pero con un solo operador: el asterisco (*). Para entenderlo mejor, analicemos el siguiente ejemplo:\n",
    "\n",
    "Supongamos que tenemos una tupla de largo desconocido y nos interesan solo el primer y último elemento, pero a su vez queremos almacenar el resto.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tupla con informacion: ('El verdadero tesoro', 21, 72, 10, 32, 99, 52, 61, 57, 'son los amigos que hicimos en el camino')\n",
      "Relleno almacenado: [21, 72, 10, 32, 99, 52, 61, 57]\n",
      "Relleno desempaquetado: 21 72 10 32 99 52 61 57\n",
      "Mensaje oculto: El verdadero tesoro son los amigos que hicimos en el camino\n"
     ]
    }
   ],
   "source": [
    "from random import randint\n",
    "\n",
    "# Creamos el relleno\n",
    "relleno = [randint(0,100) for _ in range(7, 15)]\n",
    "\n",
    "datos_limitrofes = (\n",
    "    'El verdadero tesoro',\n",
    "    *relleno, # Usamos el operador para rellenar nuestra tupla\n",
    "    'son los amigos que hicimos en el camino'\n",
    ")\n",
    "\n",
    "# Vemos como quedo nuestra tupla\n",
    "print(f'Tupla con informacion:', datos_limitrofes)\n",
    "\n",
    "# Guardamos los datos de acuerdo a lo estipulado por el ejemplo\n",
    "primer_elemento, *relleno, ultimo_elemento = datos_limitrofes\n",
    "\n",
    "# Podemos ver que el relleno se almacena en una lista\n",
    "print(f'Relleno almacenado:', relleno)\n",
    "# Y podemos aplicar el mismo operador sobre dicha lista\n",
    "# dentro de una funcion\n",
    "print(f'Relleno desempaquetado:', *relleno)\n",
    "# Imprimimos el mensaje oculto\n",
    "print(f'Mensaje oculto: {primer_elemento} {ultimo_elemento}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Con esto, ahora conocemos una funcionalidad extra que tiene el operador asterisco. Eso si, hay que tener ojo de no confundirlas. A continuacion se mostrara una ejemplicacion de cada una de ellas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "UwwwwwU\n",
      "(1, '2', '3', '4', '5', '6', 7)\n"
     ]
    }
   ],
   "source": [
    "# Su funcion que conociamos de antes: Multiplicar\n",
    "tupla_multiplicacion = ('U', 'w'*5, 'U')\n",
    "print(''.join(tupla_multiplicacion))\n",
    "\n",
    "# Su nueva funcionalidad: Desempaquetar\n",
    "tupla_desempaquetamiento = (1, *'2N3N4N5N6'.split('N'), 7)\n",
    "print(tupla_desempaquetamiento)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "lXrSWaH_QeYk"
   },
   "source": [
    "### *Slicing* de tuplas\n",
    "\n",
    "Al igual que en con listas, es posible tomar secciones de una tupla usando la notación de ***slicing***.\n",
    "\n",
    "Veamos algunos ejemplos de *slicing* aplicado a tuplas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "data: (400, 20, 1, 4, 10, 11, 12, 500)\n",
      "1. data[1:3]: (20, 1)\n",
      "2. data[3:]: (4, 10, 11, 12, 500)\n",
      "3. data[:5]: (400, 20, 1, 4, 10)\n",
      "4. data[2::2]: (1, 10, 12)\n",
      "5. data[1:6:2]: (20, 4, 11)\n",
      "6. data[::-1]: (500, 12, 11, 10, 4, 1, 20, 400)\n"
     ]
    }
   ],
   "source": [
    "# Usando los valores asignados en a, podemos obtener los valores de una sección de la tupla.\n",
    "data = (400, 20, 1, 4, 10, 11, 12, 500)\n",
    "print(f'data: {data}')\n",
    "\n",
    "# 1. Recuperamos los elementos que están entre los índices 1 y 3\n",
    "a = data[1:3]\n",
    "print(f'1. data[1:3]: {a}')\n",
    "\n",
    "# 2. Recuperamos desde el índice 3 en adelante\n",
    "a = data[3:]\n",
    "print(f'2. data[3:]: {a}')\n",
    "\n",
    "# 3. Recuperamos los valores hasta el índice 5\n",
    "a = data[:5]\n",
    "print(f'3. data[:5]: {a}')\n",
    "\n",
    "# 4. Recuperamos desde el índice 2 en adelante respecto del slice en pasos de a dos\n",
    "a = data[2::2]\n",
    "print(f'4. data[2::2]: {a}')\n",
    "\n",
    "# 5. Recuperamos entre los índices 1 y 4, en pasos de a dos\n",
    "a = data[1:6:2]\n",
    "print(f'5. data[1:6:2]: {a}')\n",
    "\n",
    "# 6. Una secuencia puede ser fácilmente invertida\n",
    "a = data[::-1]\n",
    "print(f'6. data[::-1]: {a}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "QHUD7LyIQeYr"
   },
   "source": [
    "## *Named tuples*\n",
    "\n",
    "Las [*named tuples*](https://docs.python.org/3/library/collections.html#collections.namedtuple) son estructuras que permiten definir campos para cada una de las posiciones en que han sido ingresados los datos. Son útiles como una forma de agrupar datos. Al asignar nombres semánticos a cada campo, se promueve la claridad y la explicitud del código, permitiendo comprender rápidamente la estructura y el propósito de dicho objeto.\n",
    "\n",
    "Este tipo de tupla requiere definir un objeto con los nombres de los atributos que tendrá la tupla. Para poder hacer uso de esta estructura se requiere importar el módulo `namedtuple` desde la librería `collections`. La inicialización básica de una `namedtuple` requiere un *string* con el nombre para el tipo de tupla y el nombre de los campos que tendrá, los que se pueden entregar en un *string* o una lista de *strings* como en el siguiente ejemplo:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 68,
     "output_extras": [
      {
       "item_id": 1
      }
     ]
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 641,
     "status": "ok",
     "timestamp": 1520880687215,
     "user": {
      "displayName": "Cristian Ruz",
      "photoUrl": "//lh4.googleusercontent.com/-0WhxkzATSqY/AAAAAAAAAAI/AAAAAAAAIqQ/WE7Rcv4pnN8/s50-c-k-no/photo.jpg",
      "userId": "108117710965209443485"
     },
     "user_tz": 180
    },
    "id": "EPktwjyAQeYt",
    "outputId": "d1ed7fd8-3518-4394-a49f-c4b8d2aac52a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Christian:\t13427974-5\n",
      "<class '__main__.ChileanRegister_type'>\n",
      "lucasvsj:\tdkSTiBwer\n",
      "<class '__main__.MyRegister_type'>\n"
     ]
    }
   ],
   "source": [
    "from collections import namedtuple\n",
    "from random import choices\n",
    "from string import ascii_letters\n",
    "\n",
    "\n",
    "# Asignamos un nombre a la tupla (Register_type), y los nombres de los atributos que tendrá\n",
    "ChileanRegister = namedtuple('ChileanRegister_type', ['RUT', 'name', 'age'])\n",
    "# Vemos que podemos asignar los atributos como string\n",
    "MyRegister = namedtuple('MyRegister_type', 'unique_id, nickname, age')\n",
    "\n",
    "# Instanciación e inicialización de la tupla\n",
    "c1 = ChileanRegister('13427974-5', 'Christian', 20)\n",
    "c2 = MyRegister(''.join(choices(ascii_letters, k=9)), 'lucasvsj', 5)\n",
    "\n",
    "print(f'{c1.name}:\\t{c1.RUT}')\n",
    "print(type(c1))\n",
    "print(f'{c2.nickname}:\\t{c2.unique_id}')\n",
    "print(type(c2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Al igual que las tuplas, las *named tuples* son inmutables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "can't set attribute",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[10], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m c1\u001b[38;5;241m.\u001b[39mname \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCristian\u001b[39m\u001b[38;5;124m'\u001b[39m\n",
      "\u001b[0;31mAttributeError\u001b[0m: can't set attribute"
     ]
    }
   ],
   "source": [
    "c1.name = 'Cristian'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sin embargo, existe el método `_replace`, que permite generar una copia de la `namedtuple`, remplazando los valores indicados por los nuevos valores:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ChileanRegister_type(RUT='13427974-5', name='Antonio', age=31)\n"
     ]
    }
   ],
   "source": [
    "c1_mod = c1._replace(name=\"Antonio\", age=31)\n",
    "print(c1_mod)"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "default_view": {},
   "name": "01-introduccion.ipynb",
   "provenance": [],
   "version": "0.3.2",
   "views": {}
  },
  "kernelspec": {
   "display_name": "base",
   "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.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
