Python: Estructuras de datos: Conjuntos (set)
Un conjunto (objeto set) es una colección desordenada de distintos objetos. Usos comunes incluyen pruebas de si es o no miembro del conjunto, eliminación de duplicados y cálculo de operaciones matemáticas como intersección (and), unión (or), diferencia y diferencia simétrica (xor). Para otros contenedores es mejor usar diccionarios, listas y tuple
.Al igual que otras colecciones, los conjuntos (sets) admiten x in conjunto
, len(conjunto)
y for x in conjunto
. Al ser una colección desordenada, los conjuntos no registran la posición del elemento ni el orden de inserción. En consecuencia, los conjuntos no admiten indexación, división u otro comportamiento similar a una secuencia.
Para crear un conjunto vacío NO podemos usar conjunto = {}
ya que esto crea un diccionario, para ello debemos usar conjunto = set()
.
El método conjunto.add(elemento)
añade un elemento al conjunto. Si el elemento ya existe no hace nada, el conjunto se mantiene intacto.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips1.add('10.20.30.40') ips1 {'10.20.30.40', '192.168.1.1', '3.3.3.3', '6.6.6.6', '8.8.8.8'} ips1.add('3.3.3.3') ips1 {'10.20.30.40', '192.168.1.1', '3.3.3.3', '6.6.6.6', '8.8.8.8'}
Disponemos de varios métodos para borrar un elemento o todos.
El método .remove(elemento)
elimina un elemento del conjunto. Si el elemento no existe se produce un error KeyError
, por lo que es mejor usar conjunto.discard(elemento)
.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips1.remove('192.168.1.1') ips1 {'3.3.3.3', '6.6.6.6', '8.8.8.8'} ips1.remove('10.20.30.40') --------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-4-24fcb3349e48> in <module>() ----> 1 ips1.remove('10.20.30.40') KeyError: '10.20.30.40'
El método conjunto.discard(elemento)
elimina un elemento del conjunto si existe, si no, no hace nada ni se produce error.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips1.discard('192.168.1.1') ips1 {'3.3.3.3', '6.6.6.6', '8.8.8.8'} ips1.discard('10.20.30.40') ips1 {'3.3.3.3', '6.6.6.6', '8.8.8.8'}
El método conjunto.pop()
elimina un elemento de formar arbitraria del conjunto y devuelve el elemento eliminado, si el conjunto está vacío se produce un error KeyError
.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips1.pop() '192.168.1.1' ips1.pop() '3.3.3.3' ips1.pop() '6.6.6.6' ips1.pop() '8.8.8.8' ips1.pop() --------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-6-a5d13ddad887> in <module>() ----> 1 ips1.pop() KeyError: 'pop from an empty set'
Por último si queremos vaciar un conjunto, borrando todos sus elementos, tenemos el método conjunto.clear()
.
In [1]: ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'}
In [2]: ips1.clear()
In [3]: ips1
Out[3]: set() <-- conjunto vacío
Recordemos que al igual que listas y diccionarios, los conjuntos son referenciados, por lo que si hacemos conjunto1 = conjunto2
, lo que tenemos es un mismo conjunto con dos nombres, entonces si queremos hacer una copia debemos usar el método conjunto.copy()
.
coleccion1 = set() coleccion2 = set() coleccion1.add(1) coleccion1.add(2) coleccion1.add(3) coleccion2 = coleccion1 coleccion1 {1, 2, 3} coleccion2 {1, 2, 3} coleccion2.discard(2) coleccion1 {1, 3} coleccion2 {1, 3} coleccion1.add(4) coleccion2 = coleccion1.copy() coleccion1 {1, 3, 4} coleccion2 {1, 3, 4} coleccion2.discard(3) coleccion1 {1, 3, 4} coleccion2 {1, 4}
Tenemos varios métodos para hacer comprobaciones.
El método conjunto1.isdisjoint(conjunto2)
comprueba si el conjunto1
y conjunto2
tienen elementos comunes:
c1 = {1,2,3} c2 = {3,4,5} c3 = {-1,99} c4 = {1,2,3,4,5} c1.isdisjoint(c2) False <-- tienen en común el 3 c1.isdisjoint(c3) True <-- no tienen en común ninguno
El método conjunto1.issubset(conjunto2)
comprueba si el conjunto1
es un sub conjunto de conjunto2
:
c1 = {1,2,3} c2 = {3,4,5} c3 = {-1,99} c4 = {1,2,3,4,5} c2.issubset(c1) False <-- los elementos de c2 no están en c1 c2.issubset(c4) True <-- los elementos de c2 están en c4
El método conjunto1.issuperset(conjunto2)
comprueba si el conjunto1
es un super conjunto de conjunto2
:
c1 = {1,2,3} c2 = {3,4,5} c3 = {1,99} c4 = {1,2,3,4,5} c4.issuperset(c3) False <-- todos los elementos de c3 no están en c4 c4.issuperset(c1) True <-- todos los elementos de c1 están en c4
El método conjunto.union(*others)
(o el signo |
) lo que hace es devolver un conjunto nuevo con todos los elementos únicos de todos los conjuntos
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips2 = {'8.8.8.8', '172.16.20.33', '9.9.9.9', '3.3.3.3'} ips3 = {'9.9.9.9', '10.20.30.40', '6.6.6.6', '3.3.3.3'} ips1.union(ips2) {'172.16.20.33', '192.168.1.1', '3.3.3.3', '6.6.6.6', '8.8.8.8', '9.9.9.9'} ips1 | ips2 | ips3 {'10.20.30.40', '172.16.20.33', '192.168.1.1', '3.3.3.3', '6.6.6.6', '8.8.8.8', '9.9.9.9'}
El método conjunto1.update(*others)
, también conjunto1 |= other | ...
(es un or con asignación), lo que hace es añadir al conjunto1
los elementos únicos de todos los conjuntos.
Es lo mismo que conjunto1.union(*other)
pero esta vez en vez de devolver el resultado teniendo que asignarlo a una variable, éste ya lo deja asignado a conjunto1
.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips2 = {'8.8.8.8', '172.16.20.33', '9.9.9.9', '3.3.3.3'} ips3 = {'9.9.9.9', '10.20.30.40', '6.6.6.6', '3.3.3.3'} ips1.update(ips2) ips1 {'172.16.20.33', '192.168.1.1', '3.3.3.3', '6.6.6.6', '8.8.8.8', '9.9.9.9'} ips3 |= ips2 ips3 {'10.20.30.40', '172.16.20.33', '3.3.3.3', '6.6.6.6', '8.8.8.8', '9.9.9.9'}
El método conjunto1.intersection(*others)
(o el signo &
)> lo que hace es devolver un conjunto nuevo con todos los elementos que están en todos los conjuntos (los que son comunes en todos).
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips2 = {'8.8.8.8', '172.16.20.33', '9.9.9.9', '3.3.3.3'} ips3 = {'9.9.9.9', '10.20.30.40', '6.6.6.6', '3.3.3.3'} ips1.intersection(ips2) {'3.3.3.3', '8.8.8.8'} ips1 & ips2 & ips3 {'3.3.3.3'}
El método conjunto1.intersection_update(*others)
también conjunto1 &= other & ...
(es un and con asignación), lo que hace es mantener en el conjunto1
los elementos únicos de todos los conjuntos.
Es lo mismo que conjunto1.intersection(*others
pero esta vez en vez de devolver el resultado teniendo que asignarlo a una variable, éste ya lo deja asignado a conjunto1
.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips2 = {'8.8.8.8', '172.16.20.33', '9.9.9.9', '3.3.3.3'} ips3 = {'9.9.9.9', '10.20.30.40', '6.6.6.6', '3.3.3.3'} ips1.intersection_update(ips2) ips1 {'3.3.3.3', '8.8.8.8'} ips3 &= ips2 ips3 {'3.3.3.3', '9.9.9.9'}
El método conjunto1.difference(*others)
(o el signo -
) lo que hace es devolver un conjunto nuevo solo con los elementos de conjunto1
que no están en el resto de los conjuntos. Lo que hace es quitar (restar) de conjunto1
los elementos de que coinciden de los otros conjuntos.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips2 = {'8.8.8.8', '172.16.20.33', '9.9.9.9', '3.3.3.3'} ips3 = {'9.9.9.9', '10.20.30.40', '6.6.6.6', '3.3.3.3'} ips1.difference(ips2) {'192.168.1.1', '6.6.6.6'} ips1 - ips2 - ips3 {'192.168.1.1'}
El método conjunto1.difference_update(*others)
también set -= other | ...
(es un menos con asignación), lo que hace es borrar elementos de conjunto1
que se encuentren en los otros conjuntos. Si usamos la asignación -=
se usa el signo |
para separar los conjuntos.
Es lo mismo que conjunto1.difference(*others)
pero esta vez en vez de devolver el resultado teniendo que asignarlo a una variable, éste ya lo deja asignado a conjunto1
.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips2 = {'8.8.8.8', '172.16.20.33', '9.9.9.9', '3.3.3.3'} ips3 = {'9.9.9.9', '10.20.30.40', '6.6.6.6', '3.3.3.3'} ips1.difference_update(ips2) ips1 {'192.168.1.1', '6.6.6.6'} ips3 -= ips2 | ips1 ips3 {'10.20.30.40'}
El método conjunto1.symmetric_difference(conjunto2)
(o el signo ^
, XOR) lo que hace es devolver un conjunto nuevo con los elementos que están en conjunto1
o en conjunto2
pero no en ambos.
El método solo permite operaciones entre 2 conjuntos, cuidado con el ^
(ver ejemplo).
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips2 = {'8.8.8.8', '172.16.20.33', '9.9.9.9', '3.3.3.3'} ips3 = {'9.9.9.9', '10.20.30.40', '6.6.6.6', '3.3.3.3'} ips1.symmetric_difference(ips2) {'172.16.20.33', '192.168.1.1', '6.6.6.6', '9.9.9.9'} ips1 ^ ips2 ^ ips3 {'10.20.30.40', '172.16.20.33', '192.168.1.1', '3.3.3.3'} ips2 ^ ips3 {'10.20.30.40', '172.16.20.33', '6.6.6.6', '8.8.8.8'}
ips1 ^ ips2 ^ ips3
, hay que tener en cuenta que primero hace un ips1 ^ ips2
y luego hace un resultado ^ ips3
por eso el resultado puede ser confuso.
El método conjunto11.symmetric_difference_update(*others)
también set ^= other | ...
(es un xor con asignación), lo que hace es mantener elementos del conjunto que se encuentren en alguno de los conjuntos pero no en ambos a la vez, el método solo permite operaciones entre 2 conjuntos.
ips1 = {'192.168.1.1', '3.3.3.3', '8.8.8.8', '6.6.6.6'} ips2 = {'8.8.8.8', '172.16.20.33', '9.9.9.9', '3.3.3.3'} ips3 = {'9.9.9.9', '10.20.30.40', '6.6.6.6', '3.3.3.3'} ips1.symmetric_difference_update(ips2) ips1 {'172.16.20.33', '192.168.1.1', '6.6.6.6', '9.9.9.9'} ips3 ^= ips2 ips3 {'10.20.30.40', '172.16.20.33', '6.6.6.6', '8.8.8.8'}
Más información: tipo de dato SET