One place for hosting & domains

      Como usar a função de filtro do Python


      Introdução

      A função integrada filter() do Python pode ser usada para criar um novo iterador a partir de um iterável existente (como uma list a ou um dicionário) que irá filtrar de forma eficiente os elementos usando uma função que fornecemos. Um iterável é um objeto do Python que pode “sofrer iteração”, ou seja, ele retornará itens em uma sequência para que possamos usá-la em um loop for.

      A sintaxe básica para a função filter() é:

      filter(function, iterable)
      

      Isso irá retornar um objeto de filtro, que é um iterável. Podemos usar uma função como a list() para fazer uma lista de todos os itens retornados em um objeto de filtro.

      A função filter() fornece uma forma de filtrar valores que muitas vezes pode ser mais eficiente do que uma compreensão de lista, especialmente quando começamos a trabalhar com conjuntos de dados maiores. Por exemplo, uma compreensão de lista fará uma nova lista, que irá aumentar o tempo de execução para esse processamento. Isso significa que depois que nossa compreensão de lista tiver completado a expressão, teremos duas listas na memória. Por outro lado, a função filter() fará um objeto simples que contém uma referência à lista original, a função fornecida e um índice de onde ir na lista original, que irá ocupar menos memória.

      Neste tutorial, vamos revisar quatro maneiras diferentes de usar a filter(): com duas estruturas iteráveis diferentes, com uma função lambda e sem uma função definida.

      O primeiro argumento para filter() é uma função, que usamos para decidir se é preciso incluir ou filtrar cada item. A função é chamada uma vez para cada item presente no iterável passado como segundo argumento e, cada vez que ela retorna False, o valor é abandonado. Como este argumento é uma função, podemos passar uma função normal ou fazer uso das funções lambda, especialmente quando a expressão for menos complexa.

      A seguir, está a sintaxe de uma lambda com filter():

      filter(lambda item: item[] expression, iterable)
      

      Em uma lista, como a seguinte, podemos incorporar uma função lambda com uma expressão sobre a qual queremos avaliar cada item da lista:

      creature_names = ['Sammy', 'Ashley', 'Jo', 'Olly', 'Jackie', 'Charlie']
      

      Para filtrar esta lista de forma a encontrar os nomes das nossas criaturas de aquário que começam com uma vogal, podemos executar a seguinte função lambda:

      print(list(filter(lambda x: x[0].lower() in 'aeiou', creature_names)))
      

      Aqui declaramos um item em nossa lista como x. Então, definimos nossa expressão para acessar o primeiro caractere de cada string (ou o caractere “zero”), que é x[0]. Deixar a primeira letra minúscula em cada um dos nomes garante que eles terão letras correspondentes com a string em nossa expressão, 'aeiou'.

      Por fim, passamos o iterável creature_names. Assim como na seção anterior, aplicamos list() ao resultado para criar uma lista a partir dos retornos do filter() iterador.

      O resultado será o seguinte:

      Output

      ['Ashley', 'Olly']

      Este mesmo resultado pode ser alcançado usando uma função que definimos:

      creature_names = ['Sammy', 'Ashley', 'Jo', 'Olly', 'Jackie', 'Charlie']
      
      def names_vowels(x):
        return x[0].lower() in 'aeiou'
      
      filtered_names = filter(names_vowels, creature_names)
      
      print(list(filtered_names))
      

      Nossa função names_vowels define a expressão que implementaremos para filtrar creature_names.

      Mais uma vez, o resultado seria o seguinte:

      Output

      ['Ashley', 'Olly']

      De modo geral, as funções lambda alcançam o mesmo resultado com filter() se comparadas a quando usamos uma função regular. A necessidade de definir uma função regular cresce à medida que a complexidade das expressões que filtram nossos dados aumenta e é provável que isso promova uma maior legibilidade em nosso código.

      Podemos passar None como o primeiro argumento para filter() para que o iterador retornado filtre qualquer valor que o Python considere “aparentemente falso”. No geral, o Python considera qualquer coisa com um comprimento 0 (como uma lista vazia ou uma string vazia) ou numericamente equivalente a 0 como falso, por isso o uso do termo “aparentemente falso”.

      No caso a seguir, queremos filtrar nossa lista para mostrar apenas os números de tanques em nosso aquário:

      aquarium_tanks = [11, False, 18, 21, "", 12, 34, 0, [], {}]
      

      Neste código, temos uma lista que contém inteiros, sequências vazias e um valor booleano.

      filtered_tanks = filter(None, aquarium_tanks)
      

      Usamos a função filter() com None e inserimos a lista aquarium_tanks como nosso iterável. Como passamos o None como o primeiro argumento, vamos verificar se os itens em nossa lista são considerados falsos.

      print(list(filtered_tanks))
      

      Então, nós colocamos filtered_tanks em uma função list() para que ela retorne uma lista para filtered_tanks quando imprimirmos.

      Aqui vemos que o resultado mostra apenas os inteiros. Todos os itens que foram avaliados como False, que equivalem a 0 de comprimento, foram removidos por filter():

      Output

      [11, 25, 18, 21, 12, 34]

      Nota: se não usarmos list() e imprimirmos filtered_tanks receberíamos um objeto de filtro parecido com este: <filter object at 0x7fafd5903240>. O objeto de filtro é um iterável, então poderíamos aplicar um loop sobre ele com for ou podemos usar o list() para transformá-lo em uma lista, que é o que estamos fazendo aqui porque é uma boa maneira de revisar os resultados.

      Com o None, usamos filter() para remover itens de nossa lista que foram considerados falsos.

      Quando temos uma estrutura de dados mais complexa, ainda podemos usar filter() para avaliar cada um dos itens. Por exemplo, se tivermos uma lista de dicionários, não só queremos iterar sobre cada item na lista — um dos dicionários — mas também podemos querer iterar sobre cada par de chave:valor em um dicionário para avaliar todos os dados.

      Como um exemplo, vamos considerar que temos uma lista de cada criatura em nosso aquário, juntamente com detalhes específicos sobre cada uma delas:

      aquarium_creatures = [
        {"name": "sammy", "species": "shark", "tank number": "11", "type": "fish"},
        {"name": "ashley", "species": "crab", "tank number": "25", "type": "shellfish"},
        {"name": "jo", "species": "guppy", "tank number": "18", "type": "fish"},
        {"name": "jackie", "species": "lobster", "tank number": "21", "type": "shellfish"},
        {"name": "charlie", "species": "clownfish", "tank number": "12", "type": "fish"},
        {"name": "olly", "species": "green turtle", "tank number": "34", "type": "turtle"}
      ]
      

      Queremos filtrar esses dados por uma string de pesquisa que damos à função. Para que filter() acesse cada dicionário e cada item nos dicionários, construímos uma função aninhada, como a seguinte:

      def filter_set(aquarium_creatures, search_string):
          def iterator_func(x):
              for v in x.values():
                  if search_string in v:
                      return True
              return False
          return filter(iterator_func, aquarium_creatures)
      

      Definimos uma função filter_set() que recebe aquarium_creatures e search_string como parâmetros. Em filter_set(), passamos nossa iterator_func() como a função para filter(). A função filter_set() irá retornar o iterador resultante de filter().

      A iterator_func() recebe x como um argumento, que representa um item em nossa lista (ou seja, um único dicionário).

      Em seguida, o loop for acessa os valores em cada par chave:valor em nossos dicionários e então usa uma declaração condicional para verificar se o search_string está em v, representando um valor.

      Assim como em nossos exemplos anteriores, se a expressão for avaliada como True, a função adiciona o item ao objeto de filtro. Isso será retornado depois que a função filter_set() tiver sido concluída. Posicionamos return False fora do nosso loop para que ele verifique todos os itens em cada dicionário, em vez de retornar depois de verificar somente o primeiro dicionário.

      Chamamos filter_set() com nossa lista de dicionários e a string de pesquisa para a qual queremos encontrar correspondências:

      filtered_records = filter_set(aquarium_creatures, "2")    
      

      Assim que a função tiver sido concluída, temos nosso objeto de filtro armazenado na variável filtered_records, que transformamos em uma lista e imprimimos:

      print(list(filtered_records))      
      

      Veremos o seguinte resultado a partir deste programa:

      Output

      [{'name': 'ashley', 'species': 'crab', 'tank number': '25', 'type': 'shellfish'}, {'name': 'jackie', 'species': 'lobster', 'tank number': '21', 'type': 'shellfish'}, {'name': 'charlie', 'species': 'clownfish', 'tank number': '12', 'type': 'fish'}]

      Filtramos a lista de dicionários com a string de pesquisa 2. Podemos ver que os três dicionários que incluíam um número de tanque com 2 foram retornados. Usar nossa própria função aninhada nos permitiu acessar todos os itens e comparar eficientemente cada um com a string de busca.

      Conclusão

      Neste tutorial, aprendemos as diferentes formas de usar a função filter() em Python. Agora, você é capaz de usar filter() com sua própria função, uma função lambda, ou com None para a filtragem, sendo capaz de lidar com itens de complexidades e em estruturas de dados diferentes.

      Embora neste tutorial tenhamos imprimido os resultados de filter() imediatamente em formato de lista, é provável que em nossos programas usemos o objeto filter() retornado e manipulemos ainda mais os dados.

      Se você quiser aprender mais sobre Python, confira nossa série Como programar em Python 3 e nossa página do tópico Python.



      Source link

      Cómo instalar y usar Docker Compose en Ubuntu 20.04


      Introducción

      Docker simplifica el proceso de administración de los procesos de las aplicaciones en contenedores. Aunque los contenedores son similares a las máquinas virtuales de cierta forma, son más ligeros y más sencillos en cuanto a recursos. Esto permite a los desarrolladores desglosar un entorno de aplicación en varios servicios aislados.

      Para las aplicaciones que dependen de varios servicios, organizar todos los contenedores para que se inicien, comuniquen y se apaguen juntos puede convertirse rápidamente en algo difícil de manejar. Docker Compose es una herramienta que le permite ejecutar entornos de aplicación multi contenedor según las definiciones establecidas en un archivo YAML. Se utilizan definiciones de servicio para crear entornos totalmente personalizables con varios contenedores que pueden compartir redes y volúmenes de datos.

      En esta guía, mostraremos cómo instalar Docker Compose en un servidor Ubuntu 20.04 y cómo empezar a usar esta herramienta.

      Requisitos previos

      Para seguir los pasos de este artículo, necesitará lo siguiente:

      Paso 1: Instalar Docker Compose

      Para asegurarnos de que obtenemos la versión estable más reciente de Docker Compose, descargaremos este software de su repositorio oficial de Github.

      Primero, confirmamos la versión más reciente disponible en su página de versiones. En el momento de escribir este artículo, la versión estable más reciente es 1.26.0.

      El siguiente comando descargará la versión 1.26.0 y guardará el archivo ejecutable en /usr/local/bin/docker-compose, que hará que este software esté globalmente accesible como docker-compose:

      • sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

      A continuación, estableceremos los permisos correctos para que el comando docker-compose sea ejecutable:

      • sudo chmod +x /usr/local/bin/docker-compose

      Para verificar que la instalación se realizó correctamente, puede ejecutar:

      Visualizará un resultado similar a esto:

      Output

      docker-compose version 1.26.0, build 8a1c60f6

      Docker Compose se ha instalado correctamente en su sistema. En la siguiente sección, veremos cómo configurar un archivo docker-compose.yml y obtener un entorno en contenedor listo para usarse con esta herramienta.

      Paso 2: Configurar un archivo docker-compose.yml

      Para demostrar cómo configurar un archivo docker-compose-yml y trabajar con Docker Compose, crearemos un entorno de servidor web usando la imagen Nginx oficial de Docker Hub, el registro público de Docker. Este entorno en contenedor servirá como archivo HTML estático único.

      Comience creando un nuevo directorio en su carpeta de inicio, y luego muévalo a él:

      • mkdir ~/compose-demo
      • cd ~/compose-demo

      En este directorio, configure una carpeta de aplicaciones que servirá como la raíz del documento para su entorno Nginx:

      Usando su editor de texto preferido, cree un nuevo archivo index.html en la carpeta app:

      Coloque el siguiente contenido en este archivo:

      ~/compose-demo/app/index.html

      <!doctype html>
      <html lang="en">
      <head>
          <meta charset="utf-8">
          <title>Docker Compose Demo</title>
          <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/dark.min.css">
      </head>
      <body>
      
          <h1>This is a Docker Compose Demo Page.</h1>
          <p>This content is being served by an Nginx container.</p>
      
      </body>
      </html>
      

      Guarde y cierre el archivo cuando termine. Si utiliza nano, puede hacerlo pulsando CTRL+X, Y e INTRO para confirmar.

      A continuación, cree el archivo docker-compose.yml:

      Inserte el siguiente contenido en su archivo docker-compose.yml:

      docker-compose.yml

      version: '3.7'
      services:
        web:
          image: nginx:alpine
          ports:
            - "8000:80"
          volumes:
            - ./app:/usr/share/nginx/html
      

      El archivo docker-compose.yml normalmente comienza con la definición de la versión. Esto indicará a Docker Compose qué versión de la configuración estamos usando.

      Luego tenemos el bloque services, donde configuramos los servicios que son parte de este entorno. En nuestro caso, tenemos un único servicio llamado web. Este servicio utiliza la imagen nginx:alpine y establece una redirección de puerto con la directiva ports. Todas las solicitudes en el puerto 8000 del equipo host (el sistema desde el cual está ejecutando Docker Compose) serán redirigidas al contenedor web en el puerto 80, donde se ejecutará Nginx.

      La directiva volumes creará un volumen compartido entre el equipo host y el contenedor. Esto compartirá la carpeta app local con el contenedor, y el volumen se ubicará en /usr/share/nginx/html dentro del contenedor, que luego sobreescribirá la raíz predeterminada del documento para Nginx.

      Guarde y cierre el archivo.

      Hemos creado una página demo y un archivo docker-compose.yml para crear un entorno de servidor web en contenedor que lo presentará. En el siguiente paso, abriremos este entorno con Docker Compose.

      Paso 3: Ejecutar Docker Compose

      Con el archivo docker-compose.yml implementado, ahora podemos ejecutar Docker Compose para mostrar nuestro entorno. El siguiente comando descargará las imágenes Docker necesarias, creará un contenedor para el servicio web y ejecutará el entorno en contenedor en modo segundo plano:

      Docker Compose primero buscará la imagen definida en su sistema local, y si no puede encontrar la imagen, descargará la imagen desde Docker Hub. Verá un resultado como este:

      Output

      Creating network "compose-demo_default" with the default driver Pulling web (nginx:alpine)... alpine: Pulling from library/nginx cbdbe7a5bc2a: Pull complete 10c113fb0c77: Pull complete 9ba64393807b: Pull complete c829a9c40ab2: Pull complete 61d685417b2f: Pull complete Digest: sha256:57254039c6313fe8c53f1acbf15657ec9616a813397b74b063e32443427c5502 Status: Downloaded newer image for nginx:alpine Creating compose-demo_web_1 ... done

      Su entorno ahora está funcionando en segundo plano. Para verificar que el contenedor está activo, puede ejecutar:

      Este comando le mostrará información sobre los contenedores en ejecución y su estado, además de cualquier redireccionamiento de puertos en vigor actualmente:

      Output

      Name Command State Ports ---------------------------------------------------------------------------------- compose-demo_web_1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:8000->80/tcp

      Ahora puede acceder a la aplicación demo apuntando su servidor a localhost:8000 si está ejecutando esta demo en su equipo local, o a your_server_domain_or_IP:8000 si está ejecutando esta demo en un servidor remoto.

      Verá una página como la siguiente:

      Página demo de Docker Compose

      El volumen compartido que ha configurado en el archivo docker-compose.yml mantiene los archivos de su carpeta app sincronizados con la raíz del documento del contenedor. Si realiza algún cambio al archivo index.html, serán recogidos automáicamente por el contenedor y se reflejarán en su navegador cuando vuelva a cargar la página.

      En el siguiente paso, verá cómo gestionar su entorno en contenedor con los comandos de Docker Compose.

      Paso 4: Familiarizarse con los comandos de Docker Compose

      Ha visto cómo configurar un archivo docker-compose.yml y presentar su entorno con docker-compose up. Ahora verá cómo usar los comandos de Docker Compose para administrar e interactuar con su entorno en contenedor.

      Para verificar los registros producidos por su contenedor Nginx, puede usar el comando logs:

      Visualizará un resultado similar a esto:

      Output

      Attaching to compose-demo_web_1 web_1 | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration web_1 | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/ web_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh web_1 | 10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf web_1 | 10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf web_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh web_1 | /docker-entrypoint.sh: Configuration complete; ready for start up web_1 | 172.22.0.1 - - [02/Jun/2020:10:47:13 +0000] "GET / HTTP/1.1" 200 353 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" "-"

      Si desea pausar la ejecución del entorno sin cambiar el estado actual de sus contenedores, puede usar:

      Output

      Pausing compose-demo_web_1 ... done

      Para reanudar la ejecución tras emitir una pausa:

      Output

      Unpausing compose-demo_web_1 ... done

      El comando stop finalizará la ejecución del contenedor, pero no destruirá ningún dato asociado con sus contenedores:

      Output

      Stopping compose-demo_web_1 ... done

      Si desea eliminar los contenedores, redes y volúmenes asociados con este entorno en contenedor, utilice el comando down:

      Output

      Removing compose-demo_web_1 ... done Removing network compose-demo_default

      Observe que esto no eliminará la imagen base usada por Docker Compose para hacer girar su entorno (en nuestro caso, nginx:alpine). De esta forma, siempre que vuelva a abrir su entorno con un docker-compose up, el proceso será mucho más rápido ya que la imagen ya está en su sistema.

      En el caso de que también desee eliminar la imagen base de su sistema, puede usar:

      • docker image rm nginx:alpine

      Output

      Untagged: nginx:alpine Untagged: nginx@sha256:b89a6ccbda39576ad23fd079978c967cecc6b170db6e7ff8a769bf2259a71912 Deleted: sha256:7d0cdcc60a96a5124763fddf5d534d058ad7d0d8d4c3b8be2aefedf4267d0270 Deleted: sha256:05a0eaca15d731e0029a7604ef54f0dda3b736d4e987e6ac87b91ac7aac03ab1 Deleted: sha256:c6bbc4bdac396583641cb44cd35126b2c195be8fe1ac5e6c577c14752bbe9157 Deleted: sha256:35789b1e1a362b0da8392ca7d5759ef08b9a6b7141cc1521570f984dc7905eb6 Deleted: sha256:a3efaa65ec344c882fe5d543a392a54c4ceacd1efd91662d06964211b1be4c08 Deleted: sha256:3e207b409db364b595ba862cdc12be96dcdad8e36c59a03b7b3b61c946a5741a

      Nota: Consulte nuestra guía sobre Cómo instalar y usar Docker para obtener información más detallada sobre los comandos de Docker.

      Conclusión

      En esta guía, hemos visto cómo instalar Docker Compose y configurar un entorno en contenedor basado en una imagen del servidor web Nginx. También hemos visto cómo gestionar este entorno usando comandos Compose.

      Para ver la información completa de todos los comandos docker-compose disponibles, consulte la documentación oficial.



      Source link

      Cómo usar el módulo collections en Python 3


      El autor seleccionó el COVID-19 Relief Fund para que reciba una donación como parte del programa Write for DOnations.

      Introducción

      Python 3 tiene varias estructuras de datos integradas, incluyendo tuplas, diccionarios y listas. Las estructuras de datos nos proporcionan una forma de organizar y almacenar datos. El módulo collections nos ayuda a completar y manipular las estructuras de datos de forma eficiente.

      En este tutorial, veremos tres clases del módulo collections para ayudarle a trabajar con tuples, diccionarios y listas. Usaremos namedtuples para crear tuplas con campos con nombre, defaultdict para agrupar de forma concisa la información en diccionarios, y deque para añadir elementos de forma eficiente a cualquier lado de un objeto lista.

      Para este tutorial, trabajaremos principalmente con un inventario de peces que necesitamos modificar a medida que se añaden o retiran peces a un acuario ficticio.

      Requisitos previos

      Para sacar el máximo partido a este tutorial, se recomienda que esté algo familiarizado con los tipos de datos tupla, diccionario y lista, con su sintaxis y con cómo recuperar datos desde ellos. Puede revisar estos tutoriales para encontrar la información básica necesaria:

      Añadir campos con nombre a las tuplas

      Las tuplas de Python son secuencias de elementos ordenadas de forma inmutable o inalterable. Las tuplas se utilizan frecuentemente para representar datos en columnas; por ejemplo, líneas de un archivo CSV o filas de una base de datos SQL. Un acuario puede mantener un seguimiento de su inventario de peces como una serie de tuplas.

      Una tupla de peces individual:

      ("Sammy", "shark", "tank-a")
      

      Esta tupla está compuesta por tres elementos de cadena.

      Aunque es útil en cierta forma, esta tupla no indica claramente qué representa cada uno de sus campos. En realidad, el elemento 0 es un nombre, el elemento 1 es una especie y el elemento 2 es el depósito.

      Explicación de los campos de la tupla de peces:

      nombre especie tanque
      Sammy tiburón tanque-a

      Esta tabla deja claro que cada uno de los tres elementos de la tupla tiene un significado claro.

      namedtuple del módulo collections le permite añadir nombres explícitos a cada elemento de una tupla para hacer que estos significados sean claros en su programa Python.

      Vamos a usar namedtuple para generar una clase que claramente denomine a cada elemento de la tupla de peces:

      from collections import namedtuple
      
      Fish = namedtuple("Fish", ["name", "species", "tank"])
      

      from collections import namedtuple proporciona a su programa Python acceso a la función de fábrica namedtuple. La invocación de la función namedtuple() devuelve una clase que está vinculada al nombre Fish. La función namedtuple() tiene dos argumentos: el nombre deseado de nuestra nueva clase "Fish" y una lista de elementos denominados ["name", "species", "tank"].

      Podemos usar la clase Fish para representar la tupla de peces anterior:

      sammy = Fish("Sammy", "shark", "tank-a")
      
      print(sammy)
      

      Si ejecuta este código, verá el siguiente resultado:

      Output

      Fish(name="Sammy", species="shark", tank='tank-a')

      sammy se instancia usando la clase Fish. sammy es una tupla con tres elementos claramente nombrados.

      Se puede acceder a los campos de sammy por su nombre o con un índice de tupla tradicional:

      print(sammy.species)
      print(sammy[1])
      

      Si ejecutamos estas dos invocaciones print, veremos el siguiente resultado:

      Output

      shark shark

      Acceder a .species devuelve el mismo valor que acceder al segundo elemento de sammy usando [1]​​​.

      Usar namedtuple desde el módulo collections hace que su programa sea más legible al tiempo que mantiene las propiedades importantes de una tupla (que son inmutables y están ordenadas).

      Además, la función de fábrica namedtuple añade varios métodos adicionales para las instancias de Fish.

      Utilice ._asdict() para convertir una instancia en un diccionario:

      print(sammy._asdict())
      

      Si ejecutamos print, verá un resultado como el siguiente:

      Output

      {'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}

      Invocar .asdict() en sammy devuelve una asignación de diccionario de cada uno de los tres nombres de campo con sus correspondientes valores.

      Las versiones de Python anteriores a 3.8 pueden mostrar esta línea de forma ligeramente diferente. Es posible, por ejemplo, que vea OrderedDict en vez del diccionario simple mostrado aquí.

      Nota: En Python, los métodos con guiones bajos precedentes se consideran, normalmente, “privados”. Los métodos adicionales proporcionados por namedtuple (por ejemplo, _asdict(), ._make(), ._replace(), etc.), sin embargo, son públicos.

      Recoger datos en un diccionario

      A menudo es útil recopilar datos en los diccionarios Python. defaultdict del módulo collections puede ayudarnos a ensamblar información en diccionarios de forma rápida y concisa.

      defaultdict nunca plantea un KeyError. Si no está presente una clave, defaultdict solo inserta y devuelve un valor de marcador de posición:

      from collections import defaultdict
      
      my_defaultdict = defaultdict(list)
      
      print(my_defaultdict["missing"])
      

      Si ejecutamos este código, veremos un resultado como el siguiente:

      Output

      []

      defaultdict inserta y devuelve un valor de marcador de posición en vez de lanzar un KeyError. En este caso, especificamos el valor de marcador de posición como una lista.

      Los diccionarios regulares, por el contrario, lanzarán un KeyError sobre las claves que faltan:

      my_regular_dict = {}
      
      my_regular_dict["missing"]
      

      Si ejecutamos este código, veremos un resultado como el siguiente:

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'missing'

      El diccionario regular my_regular_dict plantea un KeyError cuando intentamos acceder a una clave que no está presente.

      defaultdict se comporta de forma diferente a un diccionario regular. En vez de plantear un KeyError sobre una clave que falta, defaultdict invoca el valor de marcador de posición sin argumentos para crear un nuevo objeto. En este caso list() para crear una lista vacía.

      Continuando con nuestro ejemplo de acuario ficticio, digamos que tenemos una lista de tuplas de peces que representan el inventario de un acuario:

      fish_inventory = [
          ("Sammy", "shark", "tank-a"),
          ("Jamie", "cuttlefish", "tank-b"),
          ("Mary", "squid", "tank-a"),
      ]
      

      Existen tres peces en el acuario; sus nombres, especies y tanque se anotan en estas tres tuplas.

      Nuestro objetivo es organizar nuestro inventario por tanque; queremos conocer la lista de peces presentes en cada tanque. En otras palabras, queremos un diccionario que asigne "tank-a" a ["Jamie", "Mary"] y "tank-b" a ["Jamie"].

      Podemos usar defaultdict para agrupar los peces por tanque:

      from collections import defaultdict
      
      fish_inventory = [
          ("Sammy", "shark", "tank-a"),
          ("Jamie", "cuttlefish", "tank-b"),
          ("Mary", "squid", "tank-a"),
      ]
      fish_names_by_tank = defaultdict(list)
      for name, species, tank in fish_inventory:
          fish_names_by_tank[tank].append(name)
      
      print(fish_names_by_tank)
      

      Cuando ejecutemos este código, veremos el siguiente resultado:

      Output

      defaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})

      fish_names_by_tank se declara como un defaultdict que va por defecto a insertar list() en vez de lanzar un KeyError. Ya que esto garantiza que cada clave en fish_names_by_tank apuntará a una lista, podemos invocar libremente .append() para añadir nombres a la lista de cada tanque.

      defaultdict le ayuda aquí porque reduce la probabilidad de KeyErrors inesperados. Reducir los KeyErrors inesperados significa que su programa puede escribirse de forma más clara y con menos líneas. Más concretamente, el idioma defaultdict le permite evitar instanciar manualmente una lista vacía para cada tanque.

      Sin defaultdict, el cuerpo de bucle for podría haber tenido un aspecto más similar a este:

      More Verbose Example Without defaultdict

      ...
      
      fish_names_by_tank = {}
      for name, species, tank in fish_inventory:
          if tank not in fish_names_by_tank:
            fish_names_by_tank[tank] = []
          fish_names_by_tank[tank].append(name)
      

      Usar simplemente un diccionario regular (en vez de un defaultdict) significa que el cuerpo de bucle for siempre tiene que comprobar la existencia de tank dado en fish_names_by_tank. Solo tras haber verificado que tank ya está presente en fish_names_by_tank, o que ha sido inicializado con un [], podremos anexar el nombre del pez.

      defaultdict puede ayudar a reducir el código de plantilla cuando se completan diccionarios, porque nunca plantea un KeyError.

      Usar deque para añadir elementos de forma eficiente a cada lado de una colección

      Las listas de Python son secuencias de elementos ordenadas mutables, o alterables. Python puede anexar a listas en tiempo constante (la longitud de la lista no tiene efecto sobre el tiempo en que se tarda en anexar), pero insertar al principio de una lista puede ser más lento (el tiempo que tarda aumenta a medida que la lista aumenta).

      En términos de la notación Big O, anexar a una lista es una operación O(1) de tiempo constante. Insertar al principio de una lista, por el contrario, es más lento con el rendimiento de O(n).

      Nota: Los ingenieros de software a menudo miden el rendimiento de los procedimientos usando algo llamado la notación “Big O”. Cuando el tamaño de una entrada no tiene efecto sobre el tiempo que se tarda en realizar un procedimiento, se dice que se ejecuta en tiempo constante u O(1) (“Big O de 1”) Como ha aprendido antes, Python puede anexar a listas con un rendimiento de tiempo constante, conocido como O(1).

      A veces, el tamaño de una entrada afecta directamente a la cantidad de tiempo que se tarda en ejecutar un procedimiento. Insertar al principio de una lista Python, por ejemplo, es más lento cuantos más elementos haya en la lista. La notación Big O utiliza la letra n para representar el tamaño de la entrada. Esto significa que añadir elementos al principio de una lista Python se ejecuta en “tiempo lineal” u O(n) (“Big O de n”).

      En general, los procedimientos O(1) son más rápidos que los procedimientos O(n).

      Podemos insertar al principio de una lista Python:

      favorite_fish_list = ["Sammy", "Jamie", "Mary"]
      
      # O(n) performance
      favorite_fish_list.insert(0, "Alice")
      
      print(favorite_fish_list)
      

      Si ejecutamos lo siguiente, veremos un resultado como el siguiente:

      Output

      ['Alice', 'Sammy', 'Jamie', 'Mary']

      El método .insert(index, object) en la lista nos permite insertar "Alice" al principio de favorite_fish_list. Notablemente, sin embargo, insertar al principio de una lista tiene un rendimiento O(n). A medida que la longitud de favorite_fish_list aumenta, el tiempo para insertar un pez al principio de la lista aumentará proporcionalmente y tardará cada vez más.

      deque (pronunciado “deck”) del módulo collections es un objeto tipo listado que nos permite insertar elementos al principio o al final de una secuencia con un rendimiento de tiempo constante O(1).

      Insertar un elemento al principio de un deque:

      from collections import deque
      
      favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"])
      
      # O(1) performance
      favorite_fish_deque.appendleft("Alice")
      
      print(favorite_fish_deque)
      

      Al ejecutar este código, veremos el siguiente resultado:

      Output

      deque(['Alice', 'Sammy', 'Jamie', 'Mary'])

      Podemos instanciar un deque usando una colección de elementos preexistente, en este caso una lista de nombres de tres peces favoritos. Invocar el método appendleft de favorite_fish_deque nos permite insertar un elemento al principio de nuestra colección con un rendimiento O(1). El rendimiento O(1) significa que el tiempo que se requiere para añadir un elemento al principio de favorite_fish_deque no aumentará incluso si favorite_fish_deque tiene miles o millones de elementos.

      Nota: Aunque deque añade entradas al principio de una secuencia de forma más eficiente que una lista, deque no realiza todas sus operaciones de forma más eficiente que una lista. Por ejemplo, acceder a un elemento aleatorio en un deque tiene un rendimiento O(n), pero acceder a un elemento aleatorio en una lista tiene un rendimiento O(1). Utilice deque cuando sea importante insertar o eliminar elementos de cualquier lado de su colección rápidamente. Podrá ver una comparativa completa del rendimiento de tiempo en la wiki de Python.

      Conclusión

      El módulo collections es una parte potente de la biblioteca estándar de Python que le permite trabajar con datos de forma concisa y eficiente. Este tutorial ha cubierto tres de las clases proporcionadas por el módulo collections incluyendo namedtuple, defaultdict y deque.

      Desde aquí, puede usar la documentación del módulo collectionpara aprender más sobre otras clases y utilidades disponibles. Para obtener más información sobre Python en general, puede leer nuestra serie de tutoriales Cómo codificar en Python 3.



      Source link