Un conjunto es una colección no ordenada de objetos únicos. Python provee este tipo de datos «por defecto» al igual que otras colecciones más convencionales como las listas, tuplas y diccionarios.
Los conjuntos son ampliamente utilizados en lógica y matemática, y desde el lenguaje podemos sacar provecho de sus propiedades para crear código más eficiente y legible en menos tiempo.
Creación de un conjunto
Para crear un conjunto especificamos sus elementos entre llaves:
s = {1, 2, 3, 4}
Al igual que otras colecciones, sus miembros pueden ser de diversos tipos:
>>> s = {True, 3.14, None, False, "Hola mundo", (1, 2)}
No obstante, un conjunto no puede incluir objetos mutables como listas, diccionarios, e incluso otros conjuntos.
>>> s = {[1, 2]} Traceback (most recent call last): ... TypeError: unhashable type: 'list'
Python distingue este tipo operación de la creación de un diccionario ya que no incluye dos puntos. Sin embargo, no puede dirimir el siguiente caso:
s = {}
Por defecto, la asignación anterior crea un diccionario. Para generar un conjunto vacío, directamente creamos una instancia de la clase set
:
s = set()
De la misma forma podemos obtener un conjunto a partir de cualquier objeto iterable:
s1 = set([1, 2, 3, 4]) s2 = set(range(10))
Un set puede ser convertido a una lista y viceversa. En este último caso, los elementos duplicados son unificados.
>>> list({1, 2, 3, 4}) [1, 2, 3, 4] >>> set([1, 2, 2, 3, 4]) {1, 2, 3, 4}
Elementos
Los conjuntos son objetos mutables. Vía los métodos add()
y discard()
podemos añadir y remover un elemento indicándolo como argumento.
>>> s = {1, 2, 3, 4} >>> s.add(5) >>> s.discard(2) >>> s {1, 3, 4, 5}
Nótese que si el elemento pasado como argumento a discard()
no está dentro del conjunto es simplemente ignorado. En cambio, el método remove()
opera de forma similar pero en dicho caso lanza la excepción KeyError
.
Para determinar si un elemento pertenece a un conjunto, utilizamos la palabra reservada in
.
>>> 2 in {1, 2, 3} True >>> 4 in {1, 2, 3} False
La función clear()
elimina todos los elementos.
>>> s = {1, 2, 3, 4} >>> s.clear() >>> s set()
El método pop()
retorna un elemento en forma aleatoria (no podría ser de otra manera ya que los elementos no están ordenados). Así, el siguiente bucle imprime y remueve uno por uno los miembros de un conjunto.
while s: print(s.pop())
remove()
y pop()
lanzan la excepción KeyError
cuando un elemento no se encuentra en el conjunto o bien éste está vacío, respectivamente.
Para obtener el número de elementos aplicamos la ya conocida función len()
:
>>> len({1, 2, 3, 4}) 4
Operaciones principales
Algunas de las propiedades más interesantes de los conjuntos radican en sus operaciones principales: unión, intersección y diferencia.
La unión se realiza con el caracter |
y retorna un conjunto que contiene los elementos que se encuentran en al menos uno de los dos conjuntos involucrados en la operación.
>>> a = {1, 2, 3, 4} >>> b = {3, 4, 5, 6} >>> a | b {1, 2, 3, 4, 5, 6}
La intersección opera de forma análoga, pero con el operador &
, y retorna un nuevo conjunto con los elementos que se encuentran en ambos.
>>> a & b {3, 4}
La diferencia, por último, retorna un nuevo conjunto que contiene los elementos de a
que no están en b
.
>>> a = {1, 2, 3, 4} >>> b = {2, 3} >>> a - b {1, 4}
Dos conjuntos son iguales si y solo si contienen los mismos elementos (a esto se lo conoce como principio de extensionalidad):
>>> {1, 2, 3} == {3, 2, 1} True >>> {1, 2, 3} == {4, 5, 6} False
Otras operaciones
Se dice que B es un subconjunto de A cuando todos los elementos de aquél pertenecen también a éste. Python puede determinar esta relación vía el método issubset()
.
>>> a = {1, 2, 3, 4} >>> b = {2, 3} >>> b.issubset(a) True
Inversamente, se dice que A es un superconjunto de B.
>>> a.issuperset(b) True
La definición de estas dos relaciones nos lleva a concluir que todo conjunto es al mismo tiempo un subconjunto y un superconjunto de sí mismo.
>>> a = {1, 2, 3, 4} >>> a.issubset(a) True >>> a.issuperset(a) True
La diferencia simétrica retorna un nuevo conjunto el cual contiene los elementos que pertenecen a alguno de los dos conjuntos que participan en la operación pero no a ambos. Podría entenderse como una unión exclusiva.
>>> a = {1, 2, 3, 4} >>> b = {3, 4, 5, 6} >>> a.symmetric_difference(b) {1, 2, 5, 6}
Dada esta definición, se infiere que es indistinto el orden de los objetos:
>>> b.symmetric_difference(a) {1, 2, 5, 6}
Por último, se dice que un conjunto es disconexo respecto de otro si no comparten elementos entre sí.
>>> a = {1, 2, 3} >>> b = {3, 4, 5} >>> c = {5, 6, 7} >>> a.isdisjoint(b) False # No son disconexos ya que comparten el elemento 3. >>> a.isdisjoint(c) True # Son disconexos.
En otras palabras, dos conjuntos son disconexos si su intersección es el conjunto vacío, por lo que puede ilustrarse de la siguiente forma:
>>> def isdisjoint(a, b): ... return a & b == set() ... >>> isdisjoint(a, b) False >>> isdisjoint(a, c) True
Conjuntos inmutables
frozenset
es una implementación similar a set
pero inmutable. Es decir, comparte todas las operaciones de conjuntos provistas en este artículo a excepción de aquellas que implican alterar sus elementos (add()
, discard()
, etc.). La diferencia es análoga a la existente entre una lista y una tupla.
>>> a = frozenset({1, 2, 3}) >>> b = frozenset({3, 4, 5}) >>> a & b frozenset({3}) >>> a | b frozenset({1, 2, 3, 4, 5}) >>> a.isdisjoint(b) False
Esto permite, por ejemplo, emplear conjuntos como claves en los diccionarios:
>>> a = {1, 2, 3} >>> b = frozenset(a) >>> {a: 1} Traceback (most recent call last): ... TypeError: unhashable type: 'set' >>> {b: 1} {frozenset({1, 2, 3}): 1}
Ejemplos
¿Qué aplicaciones reales le conciernen a los conjuntos? Consideremos un programa que solicite al usuario ingresar una indeterminada cantidad de números e indique cuáles de ellos son primos.
# Solicitar entrada del usuario. numbers = input("Ingrese números separados por espacios: ") # Convertir a una lista de números enteros. numbers = [int(n) for n in numbers.split(" ")]
Ahora bien, haciendo uso de la función get_prime_numbers()
diseñada en un artículo anterior para obtener números primos, la solución convencional (empleando listas) se vería más o menos así:
prime_numbers = [n for n in numbers if n in get_prime_numbers(max(numbers))]
No obstante, si trabajamos con conjuntos, la solución es aún más corta y eficiente:
prime_numbers = numbers & get_prime_numbers(max(numbers))
Considerando la siguiente modificación:
numbers = {int(n) for n in numbers.split(" ")}
Y en la última línea de la función:
# Retorna un conjunto. return {i + 2 for i, not_crossed in enumerate(numbers[2:]) if not_crossed}
Curso online 👨💻
¡Ya lanzamos el curso oficial de Recursos Python en Udemy!
Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter
en 2024.
Consultoría 💡
Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.
Marcela Cernelli says:
Muchísimas gracias por la explicación y los ejemplos, estoy empezando en ciencia de datos por lo tanto en python, sin la colaboración de la comunidad me sería imposible.
Recursos Python says:
Hola Marcela. Me alegra que te haya servido. Saludos!
Alfredo H. Gonzalez says:
Muchas gracias por la página y los ejemplos. Creo que se ha deslizado un error: en la parte de conjuntos, la primera vez que se usa el método issuperset, éste figura con doble «p». Saludos y gracias.
Recursos Python says:
¡Gracias! Ya fue corregido 😉