├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Gustavo Juantorena 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fluent-Python-Notas 2 | #### 📚 Notas tomadas en español por [Gustavo Juantorena](https://www.linkedin.com/in/gustavo-juantorena/) durante la lectura del libro "Fluent Python, 2nd edition (O'Reilly 2022)" de Luciano Ramalho. 3 | ![image](https://avatars.githubusercontent.com/u/9216311?s=200&v=4) 4 | #### 🦎 Le recomiendo a cualquiera persona que quiera mejorar su conocimiento de Python que adquiera el libro. Pueden comprarlo entrando a [este link](https://www.amazon.com/-/es/gp/aw/d/1492056359/ref=dp_ob_neva_mobile). 5 | 6 | #### ⭐ Si te resulta de utilidad este contenido podés darle una estrella al repositorio arriba a la derecha. 7 | #### Si tenés un comentario, ya sea porque algo no se entienda o porque encontraste un error, podés [abrir un issue](https://docs.github.com/es/issues/tracking-your-work-with-issues/creating-an-issue) o un [pull request](https://www.freecodecamp.org/espanol/news/como-hacer-tu-primer-pull-request-en-github/). 8 | 9 | #### Los ejemplos de código pueden ser parcial o totalmente basados en [este repositorio que acompaña al libro](https://github.com/fluentpython/example-code-2e). 10 | 11 | ## Índice 12 | [Parte 1: Data Structures](#parte-1-data-structures) 13 | * [Capítulo 1: The Python Data Model](#capítulo-1-the-python-data-model) 14 | * [Capítulo 2: An array of sequences](#capítulo-2-an-array-of-sequences) 15 | * [Capítulo 3: Dictionaries and sets](#capítulo-3-dictionaries-and-sets) 16 | 17 | --- 18 | 19 | ## Parte 1: Data Structures 20 | 21 | ### Capítulo 1: The Python Data Model 22 | 23 | * Python es consistente. 24 | * El "[Python Data model](https://docs.python.org/es/3/reference/datamodel.html)" es una abstracción que nos permite pensar al lenguaje como un *framework*, lo que según el autor hace que luego de aprender estas sea relativamente fácil "intuir" ("informed correct guesses") como se hacen muchas cosas. 25 | * Para llegar a escribir lo que la comunidad llama código "Pythónico", podemos hacer uso de los *métodos dunder* (abreviación de *double under*, también conocidos como métodos mágicos aunque el autor prefiere no llamarlos de esta manera porque cree que de mágicos no tienen nada) los cuales poseen la forma **\_\_nombre\_\_** 26 | * Estos métodos casi nunca son llamados por nuestros objetos, sino que los llama internamente el intérprete de Python 27 | * Nosotros usamos **len(algo)**, pero en realidad se está ejecutando **algo.\_\_len\_\_\(\)** 28 | * Agregándolos a nuestros objetos obtenemos muchas funcionalidad. [Ver ejemplo del mazo de cartas](https://github.com/fluentpython/example-code-2e/blob/master/01-data-model/data-model.ipynb): 29 | 30 | ```Python 31 | import collections 32 | 33 | Card = collections.namedtuple('Card', ['rank', 'suit']) 34 | 35 | class FrenchDeck: 36 | ranks = [str(n) for n in range(2, 11)] + list('JQKA') 37 | suits = 'spades diamonds clubs hearts'.split() 38 | 39 | def __init__(self): 40 | self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks] 41 | 42 | def __len__(self): 43 | return len(self._cards) 44 | 45 | def __getitem__(self, position): 46 | return self._cards[position] 47 | ``` 48 | 49 | * Con solo agregar los métodos dunder **\_\_len\_\_** y **\_\_getitem\_\_**, nuestro objeto "gratuitamente" es iterable (ej: Uso de **for**), se puede indexar (ej: Uso de **[]**) y podemos usar el operador **in** para evaluar si contiene un elemento (para este último en algunos casos será necesitario implementar **\_\_contains\_\_**). Básicamente logramos que se comporte como una secuencia. 50 | 51 | * Continuando con los *métodos dunder* habla de emular tipos numéricos y como implementar operadores matemáticos (ej: suma, resta, valor absoluto, etc). 52 | * Da el ejemplo de crear un objeto que sea un [vector en 2D](https://github.com/fluentpython/example-code-2e/blob/28d6d033156831a77b700064997c05a40a83805f/01-data-model/vector2d.py): 53 | 54 | ```Python 55 | import math 56 | 57 | class Vector: 58 | 59 | def __init__(self, x=0, y=0): 60 | self.x = x 61 | self.y = y 62 | 63 | def __repr__(self): 64 | return f'Vector({self.x!r}, {self.y!r})' 65 | 66 | def __abs__(self): 67 | return math.hypot(self.x, self.y) 68 | 69 | def __bool__(self): 70 | return bool(abs(self)) 71 | 72 | def __add__(self, other): 73 | x = self.x + other.x 74 | y = self.y + other.y 75 | return Vector(x, y) 76 | 77 | def __mul__(self, scalar): 78 | return Vector(self.x * scalar, self.y * scalar) 79 | ``` 80 | * Acá vemos por ejemplo que con **\_\_add\_\_** podemos usar el operador **+** con nuestros objetos, o que definimos **\_\_bool\_\_** para que nuestro objeto evalúe a **False** si su módulo es 0. 81 | * También sobre **\_\_bool\_\_**: Si nuestro objeto no tiene implementado **\_\_bool\_\_** o **\_\_len\_\_** nuestra instancia se va a considerar *truthy* (o sea que evalùa a **True**), en caso de que esté **\_\_bool\_\_** tiene prioridad y si solo está **\_\_len\_\_** en caso de devolver 0 se considera *falsy* (evalúa a **False**) 82 | * También habla de **\_\_repr\_\_** y discute un poco sus diferencias con **\_\_str\_\_** (No parece ser un tema cerrado, ver [esta discusión en Stack Overflow](https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr)) 83 | 84 | --- 85 | 86 | ### Capítulo 2: An array of sequences 87 | 88 | * Muchas estructura de datos de tipo secuencias comparten un conjunto comùn de operaciones (ej: iteración, slicing, sorting y concatenación). 89 | * Habla de las **secuencias implementadas en C que vienen en la librería estándar** (*built-in*) y las divide según dos criterios: 90 | * Criterio 1 (por lo que pueden contener): 91 | * *Container sequences*: 92 | * Pueden contener elementos de distintos tipos, incluyendo otros contaners asociados. 93 | * Guarda referencias a los objetos que contiene. 94 | * Ej: `list`, `tuple` y `collections.deque`. 95 | * *Flat sequences*: 96 | * Solo contienen un tipo de dato. 97 | * Guardan el dato (NO una referencia). 98 | * Más compactas pero limitadas tipos de datos primitivos. 99 | * Ej: `str`, `bytes` y `array.array` 100 | 101 | * Criterio 2 (mutabilidad ([buen recurso simple para entenderlo](https://ellibrodepython.com/mutabilidad-python#mutabilidad))): 102 | * *Secuencias mutables*: 103 | * Permiten ser modificadas una vez creadas. 104 | * Heredan todos los métodos de las inmutables y se les agregan otros. 105 | * Ej: `list`, `bytearray`, `array.array`, `collections.deque` 106 | * *Secuencias inmutables*: 107 | * NO permiten ser modificadas una vez creadas (ojo que esto es un poco tramposo, lo va a retomar) 108 | * Ej: `tuple`, `str`, `bytes` 109 | 110 | 111 | * A contnuación cambia de tema para hablar de listas por comprensión y generadores 112 | * **Listas por comprensión** o *list comprehensions* son una manera de popular listas utilizando un `for` en una sola línea 113 | * Ej: 114 | ```Python 115 | lista_diez = [i for i in range(10)] 116 | 117 | # Es equivalente a 118 | lista_diez = [] 119 | for i in range(10): 120 | lista_diez.append(i) 121 | ``` 122 | * Advierte que sin bien se recomienda su uso y se considera Pythónico, es posible abusar del mismo por lo cual hay que tener sentido comùn. 123 | * Un aspecto interesante también es que pueden remplazar a las funciones `map` y `filter`: 124 | * Ejemplo 2-3 del libro: 125 | ```Python 126 | 127 | symbols = '$¢£¥€¤' 128 | beyond_ascii = [ord(s) for s in symbols if ord(s) > 127] 129 | 130 | # Lo mismo pero usando map y filter 131 | beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols))) 132 | ``` 133 | Coincido con el autor sobre que es más fácil usar las listas por comprensión 134 | 135 | * También se pueden hacer for anidados como en el ejemplo del mazo de cartas del cap 1 (recomienda hace el salto de linea en cada for para mejorar la legibilidad, esto no afecta al intérprete) 136 | ```Python 137 | [Card(rank, suit) for suit in self.suits 138 | for rank in self.ranks] 139 | ``` 140 | * **Generadores** 141 | * Te "devuelven" (*yields*) items de uno en uno 142 | * Esto hace que usen menos memoria que una listcomp (evitan que tengas que crearte una lista entera de una) 143 | * Se crean con paréntesis () en lugar de corchetes [] 144 | * Los explica en detalle en el cap 17 145 | 146 | * Luego entra en detalle sobre las **tuplas** 147 | * **Tuplas como registros** 148 | * Si las usamos con este objetivo no solo es importante que sean inmutables, sino que respeten el orden 149 | * Ejemplo 2-7: 150 | ```Python 151 | lax_coordinates = (33.9425, -118.408056) 152 | city, year, pop, chg, area = ('Tokyo', 2003, 32_450, 0.66, 8014) 153 | traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')] 154 | 155 | for passport in sorted(traveler_ids): 156 | print('%s/%s' % passport) 157 | ``` 158 | * **Tuplas como listas inmutables** 159 | * Ventajas: 160 | * Claridad: Si usas tuplas sabés que su largo no va a cambiar 161 | * Performance: Menos memoria que una lista y Python puede hacer ciertas optimizaciones (en el libro se dan varios ejemplos). 162 | 163 | * Cuidado! 164 | * Si bien las tuplas son secuencias inmutables, pueden contener secuencias mutables dentro y estos **sí pueden cambiar** 165 | * Ej: 166 | ```Python 167 | a = (10, 'alpha', [1, 2]) 168 | b[-1].append(99) 169 | # >> (10, 'alpha', [1, 2, 99]) 170 | ``` 171 | * **Unpacking** en Secuencias e iterables 172 | * Principal ventaja del unpacking según el autor es evitar el uso innecesario de índices para extraer elementos de las secuencias. 173 | * Funciona con cualquier iterable (incluso *iterators* que no soportan notación de índices ([])). 174 | * Único requiisito: el iterable tiene que devolver (sigo usando esta palabra como traducción de *yield*) un ítem por variable (a menos que uses el asterisco (\*) para capturar el exceso de elementos). 175 | * Usos: 176 | * **Asignación paralela**: 177 | ```Python 178 | coordenadas = (34.6037, 58.3816) 179 | latitutd, longitud = coordenadas 180 | latitud 181 | # >> 34.6037 182 | ``` 183 | * **Usar \* como prefijo de un argumento** 184 | ```Python 185 | divmod(20, 8) 186 | # >> (2, 4) 187 | t = (20, 8) 188 | divmod(*t) 189 | # >> (2, 4) 190 | ``` 191 | * **Quedarse con el exceso de items** 192 | ```Python 193 | vocales = list('aeiou') 194 | vocales 195 | # >> ['a', 'e', 'i', 'o', 'u'] 196 | a, e, *resto = vocales 197 | a 198 | # >> 'a' 199 | e 200 | # >> 'e' 201 | resto 202 | # >> ['i', 'o', 'u'] 203 | ``` 204 | * Llamados a función y sequence literals 205 | ```Python 206 | # Ejemplo de llamada a función 207 | def fun(a, b, c, d, *rest): 208 | return a, b, c, d, rest 209 | 210 | fun(*[1, 2], 3, *range(4, 7)) 211 | # >> (1, 2, 3, 4, (5, 6)) 212 | 213 | # Ejemplo con sequence literals 214 | {*range(4), 4, *(5, 6, 7)} 215 | # >> {0, 1, 2, 3, 4, 5, 6, 7} 216 | ``` 217 | * Unpacking anidado 218 | ```Python 219 | metro_areas = [ 220 | ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), 221 | ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)), 222 | ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)), 223 | ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)), 224 | ('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)), 225 | ] 226 | 227 | def main(): 228 | print(f'{"":15} | {"latitude":>9} | {"longitude":>9}') 229 | for name,_,_, (lat,lon) in metro_areas: 230 | if lon <= 0: 231 | print(f'{name:15} | {lat:9.4f} | {lon:9.4f}') 232 | 233 | main() 234 | # >> | latitude | longitude 235 | # >> Mexico City | 19.4333 | -99.1333 236 | # >> New York-Newark | 40.8086 | -74.0204 237 | # >> São Paulo | -23.5478 | -46.6358 238 | ``` 239 | * **Pattern matching** con secuencias 240 | * Es una sección nueva en esta edición del libro (y en Python existen a partir de la versión 3.10) 241 | * Discute por qué es distinto de un switch/case y sus ventajas sobre usar if/elif 242 | * Podemos hacer *destructuring* que vendría a ser un unpacking avanzado. 243 | * Ampliando el ejemplo anterior (no vuelvo a crear la lista): 244 | Ejemplo 2-10 245 | ```Python 246 | def main(): 247 | print(f'{"":15} | {"latitude":>9} | {"longitude":>9}') 248 | for record in metro_areas: 249 | match record: 250 | case [name, _, _, (lat, lon)] if lon <= 0: # Podria ser todo () o todo [] 251 | print(f'{name:15} | {lat:9.4f} | {lon:9.4f}') 252 | main() 253 | # >> | latitude | longitude 254 | # >> Mexico City | 19.4333 | -99.1333 255 | # >> New York-Newark | 40.8086 | -74.0204 256 | # >> São Paulo | -23.5478 | -46.6358 257 | ``` 258 | * Hay sintaxis propia dentro del match/case: 259 | * El símbolo \_ matchea cualquier item individual en esa posición 260 | * Se puede agregar información de tipos (ej: str(name), float(lat)). Esto se evalua en tiempo de ejecución. 261 | * Se puede leer más en los [PEP 634,635 Y 636](https://docs.python.org/3/whatsnew/3.10.html) 262 | 263 | * **Slicing** 264 | * ¿Por qué Slices and Ranges excluyen en último item? Tiene que ver con que los índices arranquen en 0. 265 | * 1. Fácil ver el largo final cuando solo se declara la posición del *stop*: range(3) y my_list[:3] tienen el mismo tamaño (3). 266 | * 2. Computar el largo solo requiere hacer *stop* - *start* 267 | * 3. Fácil separar listas en dos partes sin superposición: 268 | ```Python 269 | l = [10, 20, 30, 40, 50, 60] 270 | l[:2] # split at 2 271 | # >> [10, 20] 272 | l[2:] 273 | # >> [30, 40, 50, 60] 274 | ``` 275 | * **Slice Objects** 276 | * Podemos crearlos con `s[start:stop:step]`. 277 | * Se puede asignar a un Slice. 278 | * Un uso interesante que remarca el autor es asignar slices a variables para un paso posterior. 279 | Ejemplo 2-13: 280 | ```Python 281 | invoice = """ 282 | 0.....6.................................40........52...55........ 283 | 1909 Pimoroni PiBrella $17.50 3 $52.50 284 | 1489 6mm Tactile Switch x20 $4.95 2 $9.90 285 | 1510 Panavise Jr. - PV-201 $28.00 1 $28.00 286 | 1601 PiTFT Mini Kit 320x240 $34.95 1 $34.95 287 | """ 288 | 289 | SKU = slice(0, 6) 290 | DESCRIPTION = slice(6, 40) 291 | UNIT_PRICE = slice(40, 52) 292 | QUANTITY = slice(52, 55) 293 | ITEM_TOTAL = slice(55, None) 294 | 295 | line_items = invoice.split('\n')[2:] 296 | 297 | for item in line_items: 298 | print(item[UNIT_PRICE], item[DESCRIPTION]) 299 | 300 | # >> $17.50 imoroni PiBrella 301 | # >> $4.95 mm Tactile Switch x20 302 | # >> $28.00 anavise Jr. - PV-201 303 | # >> $34.95 iTFT Mini Kit 320x240 304 | ``` 305 | * Slicing multidimensional 306 | * El operador **[]** puede tomar múltiples índices o Slices separados por coma (es lo que usa Numpy por ejemplo, [leer más](https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating)) 307 | * Para evaluar `a[i, j]` Python llama a `a.__get_item__((i, j))` , es decir que recibe una tupla. 308 | * Existe algo llamado Ellipsis (se usa con `...`) que es reconocido por el intérprete de Python como un token. No se usa mucho en Python base (todas las secuencias son unidimesionales excepto `memoryview`), pero tiene utilidad para librerías externas como Numpy. 309 | 310 | * **Creando listas de listas** 311 | * Se puede hacer con for anidados o con [] * n 312 | * Hay que tener cuidado con la segunda forma porque podes estar apuntando a un alias y no a la lista que querias. 313 | 314 | * **Augmented assigments** 315 | * `+=` se implementa con `__iadd__` (in-place addition) 316 | * Si no esta implementado Python usa `+` 317 | * Para secuencias mutables `+=` modifica al objeto `inplace`, pero para inmutables claramente no se puede hacer. 318 | 319 | * **`list.sort` Vs. `sorted` de la librería estándar** 320 | * Principal diferencia: list.sort modifica el objeto, mientras que sorted crea una nueva lista y la retorna 321 | * De acá se deprende algo interesante que es general a la API de Python: 322 | * Si la función modifica `inplace` (o sea produce un cambio en el objeto), entonces devuelve `None` 323 | * Las dos tienen los argumentos `reverse` (ordena descendente) y `key` que está buenísimo porque te permite elegir una función que reciba un solo parámetro con la cual decidís como ordenar (ej: Si le pasas `len` evalùa el largo de cada elemento de la lista y ordena en base a eso). 324 | * Una cosa más sobre `key`, si tenemos una lista con "números" pero que algunos están como `int` y otros como `str` podemos pasarle una de stas dos funciones a `key` para que los interprete a todos como del mismo tipo (ej: `key=int`). 325 | * Ojo con el orden que lo hace en base a ASCII (entonces por ej mayúsculas van antes que minúsuculas). 326 | 327 | 328 | * **Cuando las listas no son la solución** 329 | * Acá habla de como tendemos a usar listas para todos (me sentí identificado) a pesar de no ser la mejor opción 330 | * Si la lista solo contiene números mejor usar `array.array` que es tan liviano como un array de C. 331 | * Acá se pone técnico y explica lo que es un `memoryview`, un "Un tipo de secuencia de memoria compartida que te permite manipular slices de arrays sin copiar bytes". 332 | * Dice que si vas a hacer muchas operaciones numéricas mejor usar Numpy que es el sostén de todo el stack científico en Python (coincido!) 333 | * Finalmente habla de Deques y otros Queues que son estructura de datos optimizadas para para un comportamiento FIFO (*first in first out*), o sea sacar y poner de los extremos, aunque no andan tan bien si tenes que modificar cosas en el medio de la estructura. Se puede importar de `collections` 334 | 335 | --- 336 | 337 | ### Capítulo 3: Dictionaries and sets 338 | 339 | * **Los diccionarios son una parte fundamental de Python**, no solo porque los usamos cuando programamos sino porque son fundamentales en el funcionamiento interno del lenguaje 340 | * `__builtins__.__dict__` guarda todos los tipos, objetos y funciones que vienen con la librería estándar. 341 | * Los diccionarios funcionan como [*hash tables*](https://es.wikipedia.org/wiki/Tabla_hash), lo cual los hace muy performantes. En la pàgina de Fluent Python hay [más info sobre la implementación](https://www.fluentpython.com/extra/internals-of-sets-and-dicts/) 342 | * Habla sobr el agregado de los operadores `|` y `|=` a partir de Python 3.9 343 | Ej: 344 | ```Python 345 | dict1 = {"a":1, "b":2} 346 | dict2 = {"c":3} 347 | dict1 | dict2 # Hace un 'merge' de los dos dicts creando un nuevo objeto, pero deja a dict1 y a dict2 igual 348 | ## >> {'a': 1, 'b': 2, 'c': 3} 349 | 350 | dict1 |= dict2 # Hace un 'merge' de los dos dicts pero modifica al primero (dict1) asignándole la uniòn 351 | dict1 352 | ## >> {'a': 1, 'b': 2, 'c': 3} 353 | ``` 354 | * Siguiendo con cosas "nuevas" en Python, se puede hacer Pattern matching con diccionarios. 355 | * `collection.abc` contienen las *abstract base classes (ABCs)* `Mapping` y `MutableMapping` para diccionarios y estructuras similares 356 | * El autor recomienda usar estas para verificar si es una instancia en lugar de usar `dict`, porque es más amplio. Ej: `isinstance(mi_diccionario, abc.Mapping)` 357 | 358 | * Entra mucho en detalle sobre qué pasa el insertar o actualizar un valor mutable 359 | * Dice que lo comùn si querés acceder a un valor `k` que no sabés si está en un diccionario `d`, en lugar de d[k] utilizarìas d.get(k, 'valor_default') 360 | * Propone dos opciones que considera mejores: 361 | * 1. Usar `collections.defaultdict`: Cuando lo inicializás le asignás un valor default por si `__getitem__` falla. 362 | * 2. Crear un objeto que herede de dict (en realidad dice que lo mejor es heredar de `collections.UserDict`) que implemente el método `__missing__` el cual actùa si falla `__getitem__` 363 | 364 | * Hay variaciones de dict en las que no se detiene mucho como `collections.OrderedDict` (que tiene algunas diferencias aunque desde Python 3.6 los diccionarios comunes mantienen orden), `collections.ChainMap` (sirve para mantener una lista de *mappings* y buscar como si fuera uno) y finalmente `collections.Counter` (útil para contar objetos `hasheables`) 365 | ```Python 366 | from collections import Counter 367 | lista1 = ['x','y','z','x','x','x','y', 'z'] 368 | Counter(lista1) 369 | # >> Counter({'x': 4, 'y': 2, 'z': 2}) 370 | ``` 371 | 372 | * Nombra al módulo `shelve` que sirve para hacer que un objeto persista (guardarlo) en un formato parecido a un diccionario donde los valores son objetos `pickle`. 373 | 374 | * Vuelve al tema de por qué es mejor heredar de `collections.UserDict` para crear nuestros mappings mDutables: 375 | * Si usas `dict` vas a tener que sobreescribir métodos que podrías simplemente heredar de `UserDict` sin problemas. 376 | 377 | * **Mappings inmutables** (solo se había hablado de los mutables) 378 | * Todos los objetos de tipo `mapping` que trae la librerìa estándar son mutables, pero hay situaciones donde podrìamos necesitar de aquellos inmutables. 379 | * El mòdulo `types` tiene una clase `MappingProxy` que, dado un objeto `mapping` devuelve una instancia `mappingproxy` que puede es de solo lectura pero es un proxy diinámico del mapping (quizás queda más claro con el ejemplo) 380 | Ej: 381 | ```Python 382 | from types import MappingProxyType 383 | d = {'one': 1} 384 | d_proxy = MappingProxyType(d) 385 | d_proxy 386 | # >> mappingproxy({'one': 1}) 387 | 388 | d_proxy['one'] # Podemos ver los items en d_proxy 389 | # >> 1 390 | 391 | d_proxy['two'] = 'x' # No podemos hacer asignaciones en d_proxy 392 | # >> TypeError: 'mappingproxy' object does not support item assignment 393 | 394 | d['two'] = 2 # Si modificamos el diccionario original, el cambio se ve reflejado en proxy 395 | d_proxy 396 | # >> mappingproxy({'one': 1, 'two': 2}) 397 | 398 | d_proxy['two'] 399 | # >> 2 400 | ``` 401 | 402 | * **Dictionary views** 403 | * Cuando usamos los métodos `.keys()`, `.values()` y `.items()` estamos accediendo a vistas (*views*) que son instancias de las clases `dict.keys()`, `dict.values()` y `dict.items()` respectivamente. 404 | * Son proyecciones de solo lectura usadas en la implementación de la clase `dict` 405 | * No las podemos crear o modificar, están para ser usadas por Python internamente. 406 | 407 | * **Sets** 408 | * El autor dice que se tienden a usar poco, pero que son muy útiles (coincido) 409 | * Un uso interesante es el de eliminar duplicados (`list(set(lista_con_duplicados)` devuelve una lista sin duplicados), pero da un truco interesante que es usar un diccionario para además preservar el orden (`listdict.fromkeys(lista_con_duplicados))`) 410 | * Los elementos dentro de los `sets` tienen que ser *hasheables* (por lo cual no puede haber `sets` dentro de `sets`), pero esto no vale para su versión inmutable los `frozenset`. 411 | * Para inicializar un set vacìo estamos obligados a usar `set()` porque `{}` genera un diccionario vacìo. 412 | * Hay sets comprehensions tal como existen los dict comprehensions. 413 | * Por su implementaciòn, buscar en elemento en un set es muy eficiente. 414 | * Operaciones con sets: Usando teorìa de conjuntos se puede operar con los sets para buscar intersecciones, conjunciones, etc. La API es muy rica en este sentido y muchas veces podemos obtener resultados màs eficientes en cantidad de código y tiempo de ejecución respecto a implementaciones con loops. 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | --------------------------------------------------------------------------------