Tue. Oct 4th, 2022

Buceando en el error de tipo inquebrantable

Un problema común al que se enfrentan todos los desarrolladores de Python en algún momento (o en realidad, muchas veces) es un TypeError con las palabras mágicas tipo indescifrable. Todos podemos estar de acuerdo en que no suena bien y puede llegar a asustar sin el contexto adecuado. Python es un lenguaje construido sobre diccionarios. Por ejemplo, así es como los espacios de nombres o las clases almacenan sus funciones y atributos. Comprender cómo funcionan los diccionarios más allá de la API de Python puede ayudarnos a dar sentido a esta estructura de datos y evitar errores comunes. Esta publicación profundizará en cartografía tipos, lo que hacen internamente y cómo este conocimiento nos ayuda a comprender más sobre la excepción no modificable. El acceso a los datos se puede realizar de dos maneras:Análisis completo, donde el proceso necesita pasar por todos los elementos de la estructura de datos. En Python, un ejemplo sería una Lista. Si bien podemos acceder a un elemento por índice, no hay forma de identificar directamente un elemento específico dentro del contenedor, además, los índices pueden cambiar con el tiempo.Búsqueda de claves, donde el proceso sabe cómo identificar y recuperar elementos específicos directamente. A cartografía es una estructura de datos que soporta esta estrategia de acceso a datos. Un dict sería una de las implementaciones de Python de un tipo de mapeo. La principal diferencia entre ambos enfoques es que para los mapeos, los datos almacenados en el contenedor deben contar con un llaveque se utilizará para identificar un artículo, y un valorque es el elemento que contiene los datos que queremos recuperar. Para que la búsqueda de claves sea consistente, hay un par de propiedades que deben cumplirse (si no modificamos el contenedor): Un valor solo debe ser accesible por una llave. Los valores únicos evitan colisiones y ayudan a garantizar que ningún dato sea anulado por un elemento distinto. Una clave siempre debe devolver el mismo valor durante toda la vida útil de un par clave-valor. La asignación de claves hashable a los valores almacenados en el contenedor garantiza que se mantengan las propiedades anteriores. Cuando agregamos un nuevo par a un diccionario, la clave en sí no se usará para almacenar el valor, sino el resultado de aplicarle una función hash.Hashing una clave para almacenar el valor. Imagen del autor. Siguiendo el diagrama, las funciones hash nos ayudan a obtener de forma única los valores requeridos en base a una clave para toda la vida de la clave. Imaginemos por un momento qué pasaría si las funciones hash no tuvieran estas propiedades:Hash que no mantiene las propiedades desordenadas. Imagen del autor. Comenzamos con dos claves k1 y k2 que producen el mismo hash H1. Usando k1, podríamos recuperar v1 o v2, y terminar con inconsistencia de datos. Insertar un nuevo par (k3, v3) donde k3 tiene un hash como H0, significa que perdemos el contenido de v0 ya que lo estamos sobrescribiendo con v3.

Sin las debidas garantías, obtenemos resultados inesperados e incluso podemos perder datos.

Sin embargo, las funciones hash no son las únicas involucradas en las búsquedas de claves. Los objetos clave reales también forman parte de la ecuación. Por lo tanto, para garantizar que se cumplan las dos propiedades principales de búsqueda, debemos introducir un tema importante: mutabilidadDecimos que un objeto es inmutable si su estado no se puede modificar después de su creación y mutable de lo contrario. Incluso si las funciones hash están bien definidas, los objetos mutables que mantienen varios estados durante su vida producirán resultados diferentes si aplicamos la función hash a ellos Luego, terminamos en el lío de mapeo que se muestra arriba. Por el bien de la discusión, supongamos que la longitud es una función hash adecuada. Si quisiéramos usar una lista como clave, estaríamos aplicando len(lista) para acceder al contenedor. Actualizar el estado de la lista agregando o eliminando elementos (mutando la lista) significaría que no podríamos almacenar y obtener datos correctamente de un mapeo. El ejemplo anterior muestra por qué Python solo permite inmutable objetos para convertirse en claves de mapeo. Las cadenas o los números enteros son inmutables, por lo que los desarrolladores pueden crear dictados con ellos de forma segura: pero intentar esto con tipos como list o set, genera un TypeError: unhashable type. ¿Por qué? Porque esos dos son mutables. Podemos agregar y eliminar elementos como deseemos, por lo que los resultados de la función hash variarán. El error de tipo inquebrantable nos dice que estamos tratando de crear un diccionario con un valor que no se ajusta para convertirse en una clave adecuada, ya que no podemos asegurar que la función hash siempre devolverá el mismo resultado para el mismo objeto. Mientras que str e int son las teclas de diccionario típicas; podríamos necesitar otros tipos de datos en algún momento. Afortunadamente, Python trae algunas colecciones a la mesa que son inmutables: lista frente a tupla: mientras que las listas son una colección ordenada genérica, las tuplas generalmente aportan significado y estructura a sus elementos. Además, las tuplas son inmutables y, como no se pueden actualizar, cada posición contiene una información específica (fuente recomendada). conjunto frente a conjunto congelado: ambas estructuras comparten la mayor parte de la implementación de C, pero los conjuntos congelados son inmutables. aún no se ha implementado, pero hay un PEP-603 abierto para su consideración para agregar una estructura de datos persistente para las asignaciones. A veces, es valioso y emocionante saltar a la raíz de un tema para comprender por qué Python funciona de la manera que lo hace. publicación, hemos revisado: Cómo funcionan los tipos de datos de mapeo. Cómo las funciones hash y la inmutabilidad garantizan la coherencia y la seguridad al almacenar y acceder a los datos. Diferentes tipos de Python mutables e inmutables.