2.10. Conjuntos¶
Un conjunto es una colección desordenada de elementos únicos. Añadir un valor que ya está presente no tiene efecto; la iteración produce cada valor exactamente una vez. Los conjuntos son la herramienta adecuada cuando importan la pertenencia y la eliminación de duplicados, y el orden no.
2.10.1. Crear un conjunto¶
Usa llaves para un conjunto no vacío, o set() para uno vacío:
colours = {"red", "green", "blue"}
empty = set()
Las llaves se parecen a un literal de dict; {} por sí solo es un dict vacío, no un conjunto vacío – uno de los accidentes históricos de Python. Usa set() para el caso vacío.
set() también construye un conjunto a partir de cualquier iterable, que es la forma estándar de eliminar duplicados de una secuencia:
nums = [1, 2, 2, 3, 1, 4]
unique = set(nums)
print(unique)
Salida:
{1, 2, 3, 4}
El orden de impresión puede variar – los conjuntos no garantizan iterar en ningún orden concreto.
2.10.2. Conjunto frente a diccionario¶
Tanto los conjuntos como los diccionarios almacenan elementos únicos en una tabla hash. Lo que lleva consigo cada elemento es la diferencia:
Un
dictalmacena pares clave-valor. Buscar una clave devuelve su valor.Un
setalmacena solo los elementos. Buscar un elemento te dice si está ahí.
La elección entre los dos depende de si el valor que acompaña a cada elemento significa algo:
Recurre a un conjunto cuando ningún valor corresponde junto a cada elemento – solo te importa si el elemento está presente, o estás combinando grupos de elementos únicos con unión / intersección.
Recurre a un diccionario cuando cada elemento está emparejado con datos que la búsqueda debe recuperar – un mapa de configuración, una caché, un contador indexado por nombre.
Los dos tipos comparten gran parte de la sintaxis superficial, que es de donde proviene la mayor parte de la confusión. Las diferencias en un bloque:
set |
dict |
|
|---|---|---|
contiene |
elementos únicos |
claves únicas, cada una con un valor |
literal poblado |
|
|
literal vacío |
|
|
prueba de pertenencia |
|
|
obtener un valor |
n/d |
|
añadir un elemento |
|
|
iterar |
produce elementos |
produce claves (usa |
La asimetría entre los literales poblado y vacío es la trampa que merece la pena destacar:
Las llaves con elementos dentro –
{1, 2, 3}– son un literal de conjunto; las llaves con pares clave-valor –{"a": 1}– son un literal de diccionario. El analizador los distingue por lo que hay dentro.Las llaves con nada dentro –
{}– son un diccionario vacío, no un conjunto vacío. Los diccionarios vinieron primero; el literal vacío les pertenece. Un conjunto vacío no tiene ningún literal de llaves y debe escribirseset().
Un patrón común cuando solo se leen las claves de un diccionario es cambiar a un conjunto – hace evidente la intención y elimina de la memoria los valores no utilizados.
2.10.3. Añadir y eliminar¶
set.add()– inserta un elemento.set.discard()– elimina un elemento si está presente, no hace nada si no lo está.set.remove()– elimina un elemento; lanzaKeyErrorsi no existe.set.clear()– vacía el conjunto.
s = {1, 2, 3}
s.add(4)
s.discard(99) # silent: 99 not in s
s.remove(2)
print(s)
Salida:
{1, 3, 4}
2.10.4. Pertenencia¶
El operador in comprueba la pertenencia. En un conjunto es aproximadamente de tiempo constante independientemente del tamaño – que es la razón principal para elegir un conjunto en lugar de una list cuando solo necesitas preguntar «¿está este valor ahí?»:
if "red" in colours:
print("colour is allowed")
Una list con el mismo contenido recorrería desde el principio cada vez, lo cual está bien para diez elementos pero es lento para diez mil.
2.10.5. Operaciones de conjuntos¶
Dos conjuntos pueden combinarse con las operaciones matemáticas habituales. Cada una tiene tanto una forma de operador como una forma de método:
a | boa.union(b)– todo lo que está en cualquiera de los dos conjuntos.a & boa.intersection(b)– solo lo que aparece en ambos.a - boa.difference(b)– lo que está enapero no enb.a ^ boa.symmetric_difference(b)– lo que está en uno pero no en ambos.
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)
print(a & b)
print(a - b)
print(a ^ b)
Salida:
{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{1, 2, 5, 6}
Las formas de operador son de solo lectura; las formas de método aceptan cualquier iterable a la derecha, no solo otro conjunto (a.union([5, 6])). Elige la que se lea mejor en el contexto.
2.10.6. Qué puede ir en un conjunto¶
Los elementos de un conjunto deben ser hashables – la misma restricción que las claves de un dict. int, float, str, bool, bytes y tuple (cuando su contenido es a su vez hashable) funcionan todos. list y dict no; intentar añadir uno lanza TypeError.
2.10.7. frozenset¶
Un set normal es mutable: cada llamada a add / remove / discard cambia el objeto en el sitio. Esa mutabilidad lo descalifica para ser hashable, así que un conjunto no puede usarse como clave de un dict ni como miembro de otro conjunto.
frozenset es la contraparte inmutable. Tiene las mismas búsquedas y operadores (in, |, &, -, ^) que set, pero sin add / remove ni ningún método que mute. Como nada puede cambiar nunca su contenido, el hash de un frozenset está bien definido – así que sí es hashable:
primary = frozenset({"red", "green", "blue"})
secondary = frozenset({"yellow", "purple", "orange"})
palettes = {
primary: "RGB",
secondary: "mixed",
}
print(palettes[primary])
Salida:
RGB
Construye un frozenset a partir de cualquier iterable – frozenset() para el caso vacío, frozenset(some_set) para tomar una captura inmutable de un conjunto existente:
snapshot = frozenset(s) # immutable copy of s
s.add("new") # snapshot does not change
Dos razones comunes para recurrir a él:
Uso como clave de diccionario o miembro de conjunto. Dondequiera que un único valor no pueda capturar lo que necesitas, un
frozensetde valores sí puede – «el conjunto de características que admite este controlador», «el conjunto de pines que usa este perfil».Blindar una constante. Un
frozenseta nivel de módulo de nombres permitidos no puede ser mutado accidentalmente por un llamador; unsetnormal sí. Prefierefrozensetpara cualquier cosa que deba ser de solo lectura tras su construcción.