CONJUNTOS (SET Y FROZENSET)

VOLCÁN DEL CHINYERO, RESPONSABLE DE LA ÚLTIMA ERUPCIÓN VOLCÁNICA EN TENERIFE HACIA 1909, DESDE EL VALLE DE ARRIBA, SANTIAGO DEL TEIDE, OESTE DE TENERIFE.

      Hasta ahora hemos visto tan sólo variables simples, esto es, aquéllas que almacenan en memoria un único dato y/o valor, por ejemplo, a = 1, letter = "a" o dec = 0.56. Sin embargo vamos a imaginar, tomando como base una analogía de David Sawyer McFarland, que quisiéramos crear una variable que guardara la lista de la compra: pan, agua, aceite, fruta, verduras, leche, huevos,... Si recurriéramos a las variables simples para hacerlo, tendríamos que declarar una por cada producto, de la lista de la compra: a = "pan", b = "agua", c = "aceite", d = "fruta", etc., lo que resulta muy poco práctico y, además, extremadamente latoso si nuestra lista de la compra fuera especialmente larga.
Para resolver este problema contamos en programación con las llamadas variables compuestas diseñadas para almacenar colecciones de datos. Así pues, con una única variable podríamos almacenar nuestra lista de la compra, independientemente incluso de la cantidad de productos que hubiéramos anotado en ella. Con Python contamos con un conjunto de variables compuestas diseñadas para almacenar colecciones de datos que en sí mismos constituyen un tipo específico de objeto como, recordemos, lo son  los ints, los floats y las strings. Los tipos de datos que pueden almacenar estas variables compuestas en Python y que nos vienen pintiparadas para estos casos son: las listas (tipo de dato LIST), las tuplas (tipo de dato TUPLE), los diccionarios (tipo de dato DICT) y los conjuntos (tipo de dato SET/FROZENSET). Y con cuatro, basta. Tampoco vamos a exagerar.
Cada uno de estos nuevos tipos de datos cuenta con su sintaxis y métodos característicos. De la decisión de optar por un tipo u otro para almacenar nuestra colección de datos dependerá de lo que queramos hacer con los elementos o ítems que la integran (los productos de la lista: pan, agua, aceite, etc.). Tengamos en cuenta que las colecciones de datos, al contrario de lo que sucede con las variables simples que sólo almacenan un único dato o valor, permiten añadir nuevos elementos, borrar otros, ordenarlos, sustituirlos, y algunas cosas más. En suma: las variables compuestas son aquéllas a las que se les asigna una colección de datos a través de un tipo de dato específico (list, tuple, dict y set/frozenset).


     Las strings representan un caso singular. No constituye estríctamente una colección de datos en sí misma, pero sí que contiene una "colección" de caracteres literales que pueden ser indexados, rebanados, recorridos por un bucle for/in, pueden ser sustituidos unos caracteres por otros por medio de los índices, como existen métodos para los tipos de datos de los que vamos a hablar a continuación que hacen exactamente lo mismo. 
De todas maneras, en Python, a las strings se las considera literales, que en Python remiten a números y a cadenas, propiamente dichas, que aparecen tal cual en un programa. 
Desde esta óptica, se considera a las strings como SECUENCIAS. Algunos de estos nuevos tipos de datos también son secuencias. De ellas hablaremos con más profundidad en una entrada exclusiva para ella solita. Y ahora me voy a comer mi pienso.

En muchos de los sitios que contribuyen a difundir las bondades de Python como lenguaje de programación, cuando llegan al tema de las colecciones de datos, hay unos que empiezan por las listas, otros por las tuplas, otros incluso tratan antes el tema de las HERRAMIENTAS DE CONTROL DE FLUJO, esto es, los bucles for/in y while y el condicional if/elif/else; que estudiaremos en este blog cuando terminemos con las colecciones. Pues bien, nosotros vamos a ser más originales todavía y comenzamos por los conjuntos, set, con toda seguridad, la colección de datos más infrautilizada de Python, la pobre.
Vamos juntos a reivindicarla y démosle en este modesto manual el privilegio de inaugurar las colecciones de datos.

VISTA DE GRAN CANARIA DESDE LOS MONTES DEL SUR DE ANAGA, ESTE DE TENERIFE.

SET

LAURISILVA HÚMEDA DE TAGANANA EN MEDIO DE LAS NIEBLAS DEL ALISIO, PARQUE RURAL DE ANAGA, ESTE DE TENERIFE.


        En Python contamos con un tipo de dato, set, que traducimos como "conjunto", que almacena una colección de datos. Como todos los tipos de datos, cuenta con una función homónima, la función set(), que permite la creación del mismo o conversión al mismo de otra colección de datos distinta. Un conjunto en Python, un set, es una seriación de elementos agrupados sin orden alguno (estructura de datos no ordenada) delimitados por llaves: {ítem1, ítem2, ítem3,... ítemn}, separados unos de otros por comas.
También podemos definir a un conjunto como una colección (que no secuencia) de datos de diferente tipo: int, float, string, list, tuple, etc., bien sean todos del mismo tipo o de tipos diferentes. Soporta el operador in de pertenencia o verificación, la función len() dado que un conjunto tiene medidas, tamaño, en base al número de ítems que almacena y los operadores de comparación así como los operadores de bits.




Además, los conjuntos son iterables y soportan sus propios métodos como ya veremos.


   
 
         Existen dos tipos de conjuntos: set, propiamente dicho, que es mutable y coloca los ítems en un orden aleatorio; y frozenset, que tal y como sugiere el pronombre "frozen", que se traduce como "congelado", es inmutable, es decir, que no se puede modificar. Esta es en síntesis su diferencia fundamental. Hablaremos de éste último en unos pocos párrafos.



A un conjunto sólo le podemos añadir como elementos/ítems objetos de tipo hash, que contienen un método especial, el método __hash__(), cuyo valor de retorno no varía durante toda la vida o vigencia del objeto: todos los tipos de datos inmutables, es decir, aquéllos que no se pueden modificar (float, frozenset, int, string y tuple) son objetos de tipo hash; todos los tipos de datos mutables, es decir, aquéllos que se pueden modificar (dict, list y set), no son de tipo hash, en tanto que su valor varía en función de los elementos que contengan.


Para tener las cosas un poco más clara de un sólo vistazo, proponemos la siguiente tabla:



Ya hemos dicho que los conjuntos son mutables por los que podemos añadir o eliminar elementos con facilidad, como veremos con los métodos de los conjuntos en breve, pero al mostrarse desordenados (fijémonos en el ejemplo anterior cómo al pedirle a Python por última vez que nos muestre el conjunto, éste lo hace con un orden de los ítems distinto al que nosotros empleamos para declararlo: x y "hola" intercambian posiciones), con un cierto nivel de aleatoriedad, no pueden contar con posiciones indexadas, dado que un índice apuntaría en un principio a un ítem concreto que en una posterior llamada al set podría apuntar a otro distinto. Por este motivo, un conjunto no se considera una secuencia de datos sino una colección o serie de datos (v. entrada).
Un apunte importante: al contrario de lo que podríamos hacer con una cadena o una lista, por ejemplo, no podemos crear un conjunto vacío. ¿Por qué? Porque comparte sintaxis con el objeto dict, otro objeto que almacena una colección de datos. Puestos a elegir entre uno y otro, Python decidió que si declaramos una variable a la que asignamos unas llaves vacías, ésta apunte a un objeto de tipo dict, muchísimo más utilizado en programación, antes que a un objeto de tipo set. ¡Ay!, ¡Así es la vida!



Volvamos a recordar que un conjunto, set, se puede crear con uno o  más elementos o ítems constitutivos, dispuestos en serie y separados por comas, colocados entre llaves. Estos elementos deben ser únicos: no se crean conjuntos con elementos repetidos. De hecho, una de sus aplicaciones pasa por recurrir a ellos para eliminar duplicados, por ejenplo de una lista, transformando la lista en un conjunto a través de la función integrada set() para después volver a "reconstruir" la lista a través de la función integrada list(), aunque eso sí, no lo olvidemos: nos devolverá una lista con algunos de sus elementos alterados de su orden original.



Como ya hemos mencionado, un uso típico de los conjuntos pasa por hacer uso de ellos para asegurarnos de que no procesamos datos duplicados: enviar un correo e-mail dos veces a una misma dirección porque lo tengamos repetido en nuestra lista de contactos o que, por ejemplo, almacenemos en una lista una serie de direcciones IP de los archivos de registro de un servidor web y queramos operar con cada dirección, supongamos, un vaciado de datos de una de nuestras carpetas sobre una carpeta común. Si nuestras direcciones IP son de tipo hash, se encuentran en el iterable IPS y creamos una función que invoque a cada elemento (cada dirección IP) que denominamos process_IP(), podemos efectuar el script de dos maneras distintas:



Diferencias entre A. y B.:

  1.  A. crea un conjunto que llamamos SEEN que B. no necesita.
  2.  A. procesa en el orden en que se  encuentra las direcciones IP en el iterable list con el nombre de referencia IPS, mientras que en B. se procesan todas de forma arbitraria.

El ejemplo precedente lo ofrece Mark Summerfield en el libro Python 3, publicado por la editorial Anaya Multimedia, 2010, isbn 978-84-415-26136-6, pag. 131, y cuya adquisición recomendamos encarecidamente (hay reediciones más modernas y la traducción es excelente) para quienes deseen profundizar en este lenguaje de programación.

      NO CAIGAMOS EN LA DESESPERACIÓN MÁS NEGRA, NEFASTA Y ABSOLUTA: TODO CUANTO ESTÁ ESCRITO EN ESTE ESQUEMA Y AÚN NO SE SE ENTIENDA SE VERÁ EN SU DEBIDO MOMENTO. TAN SÓLO IMPORTA PILLAR LA LÓGICA DEL ASUNTO, COMPRENDER EL ESQUEMA. CON ESO ES MÁS QUE SUFICIENTE.


ALTOS DE SANTA ÚRSULA DEJÁNDOSE ABRAZAR POR LA NUBE, NORTE DE TENERIFE.

Si quisiéramos construir un conjunto con números devueltos como strings podemos acudir directamente a la función preconstruida set() pasándoselos como argumentos. El resultado final será un conjunto de strings. Sin embargo, en ningún caso nos devolverá enteros a menos que se los pasemos como tales.



Observemos en el ejemplo cómo Python nos devuelve los valores desordenados de acuerdo a inextricables razones binarias.
Tengamos en cuenta que set() es una función que sólo acepta un único argumento, así que si queremos que nos muestre nuestra agrupación de cinco cifras como enteros, antes tendremos que crear una lista, list, a la que le asignamos una variable, siendo ésta la que pasamos como argumento de set().



Vamos a ver qué sucede cuando pasamos una string como argumento de set():



Aquí hemos utilizado una única string al modo de nuestro primer ejemplo, con tres palabras concatenadas encerradas entre comillas. Sin embargo el resultado es, a priori, sorprendente: nos devuelve el conjunto de todos los caracteres literales, sin repetir, (recordemos la utilidad del conjunto como depurador de duplicados) que componen nuestra string. Y, por supuesto, con su particular ordenamiento binario. ¿Por qué sucede esto? Muy sencillo: porque la función set() sólo devuelve conjuntos cuyos elementos sean iterables. Veamos: "123" no es iterable porque una string no es iterable,  pero "1", "2" y "3", sí; "manzanaperaplátano" no es iterable porque una string no es iterable,  pero "p", "e", "á", "t", "a", "n", "o", "r", "l", "z", "m", sí. POR TANTO, UN CONJUNTO DESGLOSA UNA STRING EN LOS CARACTERES LITERALES QUE LA COMPONEN ELIMINANDO LOS DUPLICADOS.
Si quisiéramos que el conjunto nos devolviese un conjunto con las tres frutas convenientemente separadas como elementos o ítems, tendríamos que recurrir a una lista.



Para algunos casos, podría resultar más eficaz para crear un conjunto emplear las llaves directamente antes que la función constructora set() propiamente dicha, y dejarla para las conversiones, que es realmente para lo que se utiliza.




DEGOLLADA EN EL BARRANCO DE BADAJOZ,
GÜÍMAR, SUR DE TENERIFE.


Los conjuntos pueden contener ítems de diferente tipo, como enteros, cadenas. floats, listas, tuplas, incluso otros conjuntos, en este último caso, conjuntos anidados, eso sí, como ya sabemos, separados por comas. Como ya hemos dicho, no podemos construir
directamente un conjunto vacío para contener ítems. Ya sabemos que la sintaxis  variable = {} no nos devuelve un conjunto vacío, sino otro tipo de dato diferente, un diccionario, dict. Para evitarlo no nos queda más remedio que recurrir a la función integrada set() a través de la siguiente sintaxis: variable = set(). Al pasarle la función type() veremos como ahora sí nos devuelve la respuesta que esperamos: nuestra variable almacena un conjunto.
Recordemos que esto es así en base a una suerte de jerarquía configurada con respecto a su usabilidad: los diccionarios se emplean de manera mucho más recurrente y en cierta medida son más "útiles" en programación que los conjuntos. Así que, puestos a elegir...






Ahora que ya conocemos básicamente lo que es un conjunto, set, desde el punto de vista de Python, toca hablar de algunas de sus características y métodos.
Los ítems dentro de un conjunto no tienen, como ya hemos dicho, por qué guardar un orden concreto, específico, a la hora de construirlos. Tampoco admiten la repetición o duplicación de elementos. ¿Qué tal si vemos un ejemplo?



Con los conjuntos podemos usar los operadores de pertenencia o verificación in y not in para comprobar la inclusión de tal o cual ítem en el conjunto, pero no así los índices para acceder a ellos dado que, como veremos, los conjuntos no son secuencias.


 
      TODA SECUENCIA ES UNA COLECCIÓN DE DATOS, PERO NO TODAS LAS COLECCIONES DE DATOS SON SECUENCIAS.




C122

Sí es posible, por contra, pasarle la función len(), nuestra conocida y entrañable función len() que tan buenos recuerdos nos trae, para determinar la longitud/tamaño de nuestros cojuntos.



En el caso de los conjuntos, Python cuenta con el operador & que permite devolver aquellos ítems que tengan en común dos o más conjuntos:


Percatémonos de la llaneza de su sintaxis, clara y simple. Podemos, no obstante, conseguir un resultado similar recurriendo al método intersection(), cuyo significado no hace falta traducir y que dice exactamente lo que hace y que, como podemos suponer, recurre como todos los métodos de Python a la sintaxis del punto para invocarlo: set.intersection(). Podemos verlo en funcionamiento en el siguiente ejemplo basado en el anterior:



En ambos casos, tanto con el operador & como con el método set.intersection() funciona la propiedad conmutativa: cualquiera que sea el orden que propongamos el resultado es el mismo.
La intersección de conjuntos, esto es, la creación de un subconjunto que se repiten en dos o más conjuntos tiene, por ejemplo, una gran utilidad a la hora de cruzar datos en una base de datos. Imaginemos, por ejemplo, que en la base de datos del área de gestión tributaria de un municipio x se crean varios conjuntos: por ejemplo, A, alquiler_de_vados y B, rodaje. A su vez, cada uno de ellos, esto es, A y B, contienen dos subconjuntos: A1, recibos_pagados y A2, recibos_pendientes; B1, recibos_pagados y B2, recibos_pendientes. Con las intersecciones, podremos saber qué vecinos del mencionado municipio x están al tanto de sus obligaciones tributarias a tal fecha, y si lo están en ambos impuestos o sólo en uno de ellos y cuáles no.

VISTA DEL ROQUE DE JAMA, EN EL CENTRO DE LA FOTO. UN DOMO VOLCÁNICO EN EL SUROESTE  DE LA ISLA DE TENERIFE

MÉTODOS DE SET.


      1) SET.ADD(ITEM):

      Con esta función, tal y como indica su nombre, añadimos un único elemento más al conjunto:


1. Aquí traemos la aplicación correcta del método, donde se nos devuelve el conjunto c1 con el nuevo ítem añadido.
2. En este caso introducimos deliberadamente un error cuando tratamos de añadir dos ítems nuevos al conjunto. En esta ocasión, Python lanzará una excepción de error de tipo, TypeError, en la que nos recuerda que el método lleva tan sólo un único argumento. Recordemos que si queremos añadir más de un elemento podemos recurrir al operador | de los conjuntos:



3. Empecinados como estamos en introducir dos nuevos ítems en c1, probamos ahora a separar mediante comas sendas llamadas al método, pasando como es obvio un único argumento a cada una de ellas tal y como demanda la sintaxis del método.
El resultado que nos devuelve es (None, None). Esto ocurre porque, a pesar de que las sintaxis son correctas, no lo es incluir ambas funciones en una misma línea de código de la manera en que lo hemos hecho: separando las funciones entre comas. Ante las disyuntiva, Python no puede mostrar valor alguno y decide que no hay nada, que no existe valor alguno y devuelve una tupla con el valor None para ambas funciones.

     Repasemos un poco lo que decíamos sobre el tipo de dato None en su entrada correspondiente: None constituye el único valor que contiene el tipo de dato NoneType, Lo vemos en el ejemplo cuando le pasamos la función type(). None es un tipo especial de objeto que se utiliza, por parte del programador, para asignar un valor nulo a un objeto (lista, conjunto, variable, etc.). O desde la perspectiva de la máquina, como aviso de que no puede mostrar un resultado determinado (por ejemplo, cuando se genera un "código muerto", que es aquél en que ninguna de sus condiciones es verdadera, aún cuando su sintaxis es impecable, y en consecuencia Python no nos puede mostrar resultado alguno).
El valor None nos resulta de gran utilidad en el momento en que creamos una variable a la que aún no queremos asignar un valor, normalmente, porque esto ocurrirá a lo largo de la ejecución del programa. Es como si a primera hora de la mañana creamos una lista del 'súper', la guardamos como nota en nuestro smartphone y luego, a lo largo del día, cuando se nos vayan ocurriendo cosas, las vamos añadiendo: leche, arroz, fruta, verduras, etc. Y, claro, como tenemos que guardar, a esa primera hora de la mañana como decimos, un nombre para la lista y, al menos, un ítem, creamos como variable "lista_compra" y como único ítem, None. La fórmula sería lista_compra = None. Como vemos su uso más habitual desde el punto de vista del programador es el de inicializar variables así como inicializar objetos cuando no se conocen a priori los ítems que va a contener ¿Hay smartphones para perros? ¡Yo quiero uno!

¿Cómo podemos, pues, añadir dos o más métodos en una misma línea de código? ¿Es posible hacerlo? Sí, sí que es posible: usando puntos y comas. Veámoslo en el ejemplo que mostramos a continuación:



4. Finalmente, y para continuar con nuestra tradición de ir incorporando poco a poco comandos nuevos aprovechando los ejemplos, aprovechamos para construir una sintaxis copulativa con el operador and, ("y", en castellano) de tipo 'a y b' y que, como vemos, nos resuelve la cuestión: ya tenemos nuestros dos nuevos ítems perfectamente integrados en nuestro conjunto c1.

      2) SET.CLEAR():

     Este método borra todos...¡todos! los elementos de un conjunto y nos lo devuelve vacío, como el programador lo trajo al mundo:


      3) SET.COPY():

      Con este método obtenemos una copia, (copy), superficial (shallow) del conjunto original;



CASONA SOLARIEGA EN TIJOCO BAJO,  MUNICIPIO DE ADEJE, SUROESTE DE TENERIFE

      4) SET.DIFFERENCE(SETS):

     En esta ocasión contamos con una doble opción, como en el caso de '&/set.intersection()', y es que también aquí podemos escoger entre usar el operador aritmético de resta, -, o el método del que estamos hablando en este apartado. En ambos casos, se nos devuelve un nuevo conjunto con los ítems que no se repitan entre los conjuntos, justo lo contrario que el método set.intersection(). Igualmente, podemos pasar como argumentos los conjuntos que estimemos necesarios.


1. Fijémonos cómo aquí el resultado, indistíntamente de que se recurra al método o al operador aritmético -, es el mismo.
2. Sin embargo, cuando contamos con más de un argumento, la cosa cambia: c1 - c2 - c3 muestra el mismo resultado que c1 - c2 (o viceversa). Y es que no es lo mismo (la conmutabilidad sirve para las adiciones pero, como es lógico, no para las sustracciones) restar c1 - c2 que c2 - c1 y, después, al resultado, c3. Lo vemos mucho más claro en el ejemplo siguiente:



En cambio, la aplicación del método si nos proporciona un resultado correcto, lo que incide una vez más en preferir el uso de las funciones al uso de los operadores que, en algunas ocasiones, pueden llevarnos al error.

      5) SET.SYMMETRIC_DIFFERENCE(SET):

     Se le conoce también como "unión exclusiva"y ejecuta la misma acción que el operador ^. Este método devuelve un conjunto nuevo con los elementos de  los conjuntos a y b, pero excluyendo los elementos que se repiten en ambos.




      En  el sitio web: elclubdelautodidacta.es/wp/indice-python, de don Javier Montero Gabarró se nos ofrece un excelente consejo: "conoce bien las estructuras de datos y el tipo de operaciones que puedes realizar con ellas. Con frecuencia, resolver elegántemente un problema de programación no se reduce a otra cosa sino a elegir la estructura de datos más adecuada":
Se puede decir más alto pero no más claro.




      6) SET.DISCARD(ITEM A ELIMINAR):

     Elimina un único elemento del conjunto siempre y cuando dicho elemento forme parte del mismo:



Fijémonos cómo en el caso 1., cuando aplicamos al nuevo set a el método set.discard() con un ítem inexistente, al pedirle a Python que nos devuelva el resultado nos muestra al conjunto a tal cual, ignorando nuestra "metedura de pata".

      7) SET.INTERSECTION(CONJUNTOS):

     La aplicación de este método genera el mismo resultado que el uso del operador &, al que tuvimos ocasión de conocer hace bien poco, así que este apartado nos vendrá muy bien como repaso. En ambos casos se devuelve un conjunto nuevo con los elementos comunes que se encuentren en los conjuntos a los que les pasemos el operador o el método. 



En esta ocasión, tanto al aplicar el operador como el método, si no encuentra elementos en común para los conjuntos pasados como argumentos o evaluados por el operador, devuelve un conjunto vacío. Y como podemos ver, el método set.intersection() admite más de un argumento.

      8) SET.ISDISJOINT(CONJUNTO):

    Se trata de un método booleano (recordemos  ahora de cuando estudiamos los métodos de las strings que aquéllos que empezaban por 'is'  Y nos lo podemos imaginar mejor como una pregunta: "¿es tal cosa tal cosa?". Estas preguntas, en todos los casos, se responde con un valor booleano: es o no es. True o False. Aquí sucede lo mismo.) que devuelve True si ambos conjuntos no tienen elementos en común, y False en caso contrario. De hecho, 'disjoint' significa en castellano "disjunto", que apunta en matemáticas a dos conjuntos que no tienen ningún elemento en común.



      9) SET.ISSUBSET(CONJUNTO):

     Otro método booleano más que devuelve True si el conjunto set es el mismo conjunto es el mismo conjunto o un subconjunto ("subset") del segundo conjunto que pasamos como argumento del método. Podemos aplicar primeramente un operador de comparación < o <= para comprobar a es menor o igual que b. 


FLOR DE LA PALOMERA, TÍPICA DE LOS MONTES HÚMEDOS DE TENERIFE

      10) SET.ISSUPERSET(CONJUNTO):

      Seguimos con los métodos booleanos. En este caso devuelve True si set es el mismo conjunto o un superconjunto ("superset") del segundo conjunto que pasemos como argumento del método. Podemos aplicar previamente un operador de comparación > o >= para comprobar si a es mayor o igual que b.



      11) SET.POP():

      Tenemos un nuevo método de los conjuntos, en este caso, sin argumentos que elimina y devuelve un ítem o elemento ALEATORIO del conjunto. Cuando pedimos que se nos muestre el conjunto resultante Python nos lo devolverá con el elemento que Python ha elegido arbitrariamente eliminado.
Obviamente, si le pasamos el método a un conjunto vacío nos devolverá un error.


      12) SET.REMOVE(ITEM):

      Este método realiza una función similar al método anterior sólo que esta vez elimina el ítem que le pasemos como argumento.


En este caso, 1. nos devuelve una excepción de TypeError porque pasamos más de un argumento. En el caso 2. nos devuelve una excepción de KeyError por pasarle un ítem que no está contenido en el conjunto. Y, finalmente, en el caso 3.  nos devuelve una excepción... ¡Ah! ¿A que no caímos en la cuenta de que dos llaves vacías no representan un conjunto vacío sino un diccionario vacío? ¿Eh? A quienes lo hayan cogido, felicidades por su memoria y su perspicacia. Por eso mismo nos devuelve un error de atributo, AttributeError, dado que el objeto diccionario, dict, no soporta el método set.remove().

      13) SET.UNION(CONJUNTO):

     Este método es perfectamente asimilable al uso del operador de unión |. y devuelve un conjunto nuevo integrado por todos lo ítems que no se repitan en ellos.



       14) SET.UPDATE(CONJUNTOS):

     Este último método añade a set todos los elementos de los conjuntos que se le pasen como argumentos, sin repetir ninguno, que es una de las características principales, recordemos, de los conjuntos.



VISTA PANORÁMICA DE LA LOCALIDAD NORTEÑA DE ICOD DE LOS VINOS. EN EL CUADRANTE INFERIOR DERECHO PODEMOS APRECIAR LAS CONSTRUCCIONES MÁS ANTIGUAS., CAPITAL DE LA COMARCA DE LA ISLA BAJA, NOROESTE DE TENERIFE.

CONJUNTOS POR COMPRENSIÓN:

Esto no significa otra cosa que una opción más que nos brinda nuestro amigo Python para crear un conjunto de una manera alternativa a las que ya conocemos "comprimiendo" la sintaxis, esto es, sin necesidad de escribir más líneas de código, indentaciones, etc. ¡A la porra con todo eso!: todojuntoen unamismalíneadecódigo... y ya está. ¿Mola o no mola?

      
      Hola, otra vez. De esto me he enterado yo solita a través del Guausapp. Para aprovechar esta característica de los conjuntos, acudimos a una expresión sintáctica, a una sintaxis que, como ya sabemos, determina la forma correcta de escribir una línea de código para hacérsela comprensible al intérprete de Python, que requiere de una 'expresión' y un 'bucle', normalmente, for/in, y al que puede seguir o no una condición, todo ello embutido entre llaves. Esto nos proporciona dos formulaciones sintácticas posibles:
  1. CON LA CONDICIÓN FINAL:
          Variable = {EXPRESIÓN for ÍTEM in ITERABLE (lista, conjunto, etc.) if CONDICIÓN}

      2. SIN LA CONDICIÓN FINAL:

          Variable = {EXPRESIÓN for ÍTEM in ITERABLE (lista, conjunto, etc.)}

Estudiemos el siguiente ejemplo tomado de Python3, Mike Summerfield, ed. Anaya Multimedia, pag. 132:


Tenemos una lista de archivos html (a.html, b.html, c.HTML, d.Html, e.htm y f.HTM) a la que llamamos 'files'  que pasamos al conjunto. A este conjunto le asignamos el identificador de variable HTML. Para crear el conjunto con la sintaxis de comprensión, abrimos una llave y escribimos 'x', que será la expresión, esto es, las strings que tenemos como ítems de la lista (list) 'files'. A continuación colocamos el bucle for/in, que veremos en unos pocos capítulos más adelante, cuando nos adentremos en el capítulo dedicado a las herramientas de control de flujo, donde lo trataremos con profusión y profundidad. Podemos leer el bucle de la siguiente manera: "para cada x (ítem en 'files') en la lista 'files' (iterable). Ahora le pasamos un condicional para que nuestro conjunto albergue solamente aquéllas x que cumplan una determinada condición (o condiciones). ¿Cuál es ésta (o éstas)? Pues los dos métodos de las strings (porque strings son los ítems, las x de nuestra lista 'files') que le pasamos: str.lower() y str.endswith(".html", ".htm") que ya conocemos del capítulo dedicado a los métodos de las strings. Fijémonos que lo hemos escrito uno a continuación del otro, sin necesidad de repetir el str x y con el método, notación o sintaxis del punto (dott notation o dott method) en medio, lo que significa que al resultado de la aplicación del método str.lower(), que consigue que todas las mayúsculas se vuelvan minúsculas. Y a este resultado le aplicamos a continuación el método str.endswith(suffix) para que apunte a todas aquellas strings que terminen con .html o .htm. Aquí tenemos de rondón un ejemplo magnífico de concatenación de métodos: primero aplicamos uno y, a su resultado, le aplicamos el siguiente, y al resultado le aplicamos otro, y así sucesivamente, según nuestras necesidades, separados por el operador punto. Es decir: "teniendo presente todas las expresiones que tenemos (x) bien guardaditas en nuestra lista 'files', pues para aquéllas (x) que estén en 'files', que sabemos que son strings,  les vamos a aplicar la siguiente condición: el método str.lower() primero, y al resultado que nos dé, y con el operador de punto, le aplicamos un segundo método, str.endswith(suffix). Y a las que cumplan esta condición las almacenamos en el conjunto HTML."
¡Y ya está! Un poquito pesado sí que es, la verdad, explicado así. Pero es muy sencillo de aplicar y muy útil en ocasiones. Imaginemos qué sucedería si este conjunto de comprensión tiene como elementos otros conjuntos de comprensión, que a su vez, contienen otros... ¡Casi reduciríamos un bloque completo de código con varias docenas de líneas a tan sólo ocho o diez!

ALMENDROS EN FLOR A MEDIADOS DE FEBRERO, SOBRE TABLEROS O BANCALES EN LA PEDANÍA DE ARGUAYO, OESTE DE TENERIFE.

FROZENSET

El frozenset es un conjunto fijo, de hecho "frozen" se traduce al castellano como "congelado", un tipo de set, de conjunto, de carácter inmutable, es decir, que una vez que lo hemos creado, al contrario de lo que sucede en set que hemos estudiado hasta ahora, NO se puede modificar. Dispone de una sintaxis particular:

                                                                                       frozenset(iterable)

Ello se debe a que la función frozenset(), también hay quien prefiere llamarlo "constructor", demanda un único argumento. Y este argumento debe ser un ITERABLE, es decir, cualquier objeto (un conjunto, una tupla, una cadena, una lista) que puede ser recorrido de principio a fin, por todos y cada uno de los ítems que lo constituyen (al objeto) en orden de izquierda a derecha, desde un bucle for/in.
El hecho de que al momento de devolverse el resultado nos aparezca el iterable entre paréntesis es porque estos, los paréntesis, son precisamente las "etiquetas" que utiliza Python para decirnos que aquéllo que se guarda en su interior es INMUTABLE, que no se puede modificar, como los argumentos de las funciones o los de las tuplas, de las que hablaremos cuando superemos el capítulo de las listas y sus métodos que viene a continuación, y que se representan con paréntesis. De hecho, un frozenset, mutatis mutandis, puede considerarse también como un tipo especial de tupla.
No olvidemos que una string, como cadena o serie de caracteres literarios que es, también constituye un objeto iterable y, en consecuencia, susceptible de ser pasado como argumento de una función frozenset().




Y no olvidemos tampoco que si pasamos una string literaria, con espacios en blanco, como por ejemplo una frase cualquiera o el archiconocido mantra de inicio de manual de programador "Hola Mundo", tanto set() como frozenset() incorporarán un espacio en blanco, como carácter literario que es aunque esté vacío, como un ítem más.
La función frozenset() admite aquéllos métodos  de set que no afecten en su resultado a la estructura original del conjunto dado que, insistimos, frozenset() es inmutable, no se puede modificar.
Este es el listado de los métodos que soporta:
frozenset.copy(), frozenset.difference() o el operador -, frozenset.intersection() o el operador &. Por supuesto, soporta todos los métodos booleanos: frozenset.isdisjoint(), frozenset.issubset() y frozenset.superset(). Además soporta también los métodos frozenset.union() o el operador | y frozenset.symmetric_difference() o el operador ^.
Si utilizamos un operador binario (v. tabla correspondiente) entre un conjunto set y un conjunto fijo frozenset, el resultado será el mismo que que el tipo de objeto de la izquierda: set & frozenset devuelve set; frozenset & set devuelve frozenset.



En el caso de los operadores de comparación == y !=, ambos devolverán como resultado True si ambos contienen los mismos elementos.



¿Recordamos cómo al empezar a hablar de los conjuntos, nos llamaba la atención cómo cuando le pasábamos una string como argumento nos devolvía sus caracteres literales, uno por uno, como hemos dicho recientemente? Pues ya conocemos la razón: Porque una string es una SECUENCIA.


Aprendamos más sobre ella en la siguiente entrada:

T1. SECUENCIAS EN PYTHON. COMO JUGAR A LA OCA.

Y como no puede ser de otra manera, practiquemos un poco cuanto hemos aprendido hasta ahora con los siguientes ejercicios que proponemos:


Las soluciones las tenemos en la siguiente entrada:

T4. BLOQUE DE EJERCICIOS 3. SOLUCIONES.


FRUTO DEL CARDÓN, FORTALEZA DE MASCA, MACIZO DE TENO, NOROESTE DE TENERIFE.


No hay comentarios:

Publicar un comentario