One place for hosting & domains

      Module

      How To Use the pathlib Module to Manipulate Filesystem Paths in Python 3


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Python 3 includes the pathlib module for manipulating filesystem paths agnostically whatever the operating system. pathlib is similar to the os.path module, but pathlib offers a higher level—and often times more convenient—interface than os.path.

      We can identify files on a computer with hierarchical paths. For example, we might identify the file wave.txt on a computer with this path: /Users/sammy/ocean/wave.txt. Operating systems represent paths slightly differently. Windows might represent the path to the wave.txt file like C:Userssammyoceanwave.txt.

      You might find the pathlib module useful if in your Python program you are creating or moving files on the filesystem, listing files on the filesystem that all match a given extension or pattern, or creating operating system appropriate file paths based on collections of raw strings. While you might be able to use other tools (like the os.path module) to accomplish many of these tasks, the pathlib module allows you to perform these operations with a high degree of readability and minimal amount of code.

      In this tutorial, we’ll go over some of the ways to use the pathlib module to represent and manipulate filesystem paths.

      Prerequisites

      To get the most out of this tutorial, it is recommended to have some familiarity with programming in Python 3. You can review these tutorials for the necessary background information:

      Constructing Path Instances

      The pathlib module provides several classes, but one of the most important is the Path class. Instances of the Path class represent a path to a file or directory on our computer’s filesystem.

      For example, the following code instantiates a Path instance that represents part of the path to a wave.txt file:

      from pathlib import Path
      
      wave = Path("ocean", "wave.txt")
      print(wave)
      

      If we run this code, we’ll receive output like the following:

      Output

      ocean/wave.txt

      from pathlib import Path makes the Path class available to our program. Then Path("ocean", "wave.txt") instantiates a new Path instance. Printing the output shows that Python has added the appropriate operating system separator of / between the two path components we gave it: "ocean" and "wave.txt".

      Note: Depending on your operating system, your output may vary slightly from the example outputs shown in this tutorial. If you are running Windows, for example, your output for this first example might look like oceanwave.txt.

      Currently, the Path object assigned to the wave variable contains a relative path. In other words, ocean/wave.txt might exist in several places on our filesystem. As an example, it may exist in /Users/user_1/ocean/wave.txt or /Users/user_2/research/ocean/wave.txt, but we haven’t specified exactly which one we are referring to. An absolute path, by contrast, unambiguously refers to one location on the filesystem.

      You can use Path.home() to get the absolute path to the home directory of the current user:

      home = Path.home()
      wave_absolute = Path(home, "ocean", "wave.txt")
      print(home)
      print(wave_absolute)
      

      If we run this code, we’ll receive output roughly like the following:

      Output

      /Users/sammy /Users/sammy/ocean/wave.txt

      Note: As mentioned earlier, your output will vary depending on your operating system. Your home directory, of course, will also be different than /Users/sammy.

      Path.home() returns a Path instance with an absolute path to the current user’s home directory. We then pass in this Path instance and the strings "ocean" and "wave.txt" into another Path constructor to create an absolute path to the wave.txt file. The output shows the first line is the home directory, and the second line is the home directory plus ocean/wave.txt.

      This example also illustrates an important feature of the Path class: the Path constructor accepts both strings and preexisting Path objects.

      Let’s look at the support of both strings and Path objects in the Path constructor a little more closely:

      shark = Path(Path.home(), "ocean", "animals", Path("fish", "shark.txt"))
      print(shark)
      

      If we run this Python code, we’ll receive output similar to the following:

      Output

      /Users/sammy/ocean/animals/fish/shark.txt

      shark is a Path to a file that we constructed using both Path objects (Path.home() and Path("fish", "shark.txt")) and strings ("ocean" and "animals"). The Path constructor intelligently handles both types of objects and cleanly joins them using the appropriate operating system separator, in this case /.

      Accessing File Attributes

      Now that we’ve learned how to construct Path instances, let’s review how you can use those instances to access information about a file.

      We can use the name and suffix attributes to access file names and file suffixes:

      wave = Path("ocean", "wave.txt")
      print(wave)
      print(wave.name)
      print(wave.suffix)
      

      Running this code, we’ll receive output similar to the following:

      Output

      /Users/sammy/ocean/wave.txt wave.txt .txt

      This output shows that the name of the file at the end of our path is wave.txt and the suffix of that file is .txt.

      Path instances also offer the with_name function that allow you to seamlessly create a new Path object with a different name:

      wave = Path("ocean", "wave.txt")
      tides = wave.with_name("tides.txt")
      print(wave)
      print(tides)
      

      If we run this, we’ll receive output like the following:

      ocean/wave.txt
      ocean/tides.txt
      

      The code first constructs a Path instance that points to a file named wave.txt. Then, we call the with_name method on wave to return a second Path instance that points to a new file named tides.txt. The ocean/ directory portion of the path remains unchanged, leaving the final path as ocean/tides.txt

      Accessing Ancestors

      Sometimes it is useful to access directories that contain a given path. Let’s consider an example:

      shark = Path("ocean", "animals", "fish", "shark.txt")
      print(shark)
      print(shark.parent)
      

      If we run this code, we’ll receive output that looks like the following:

      Output

      ocean/animals/fish/shark.txt ocean/animals/fish

      The parent attribute on a Path instance returns the most immediate ancestor of a given file path. In this case, it returns the directory that contains the shark.txt file: ocean/animals/fish.

      We can access the parent attribute multiple times in a row to traverse up the ancestry tree of a given file:

      shark = Path("ocean", "animals", "fish", "shark.txt")
      print(shark)
      print(shark.parent.parent)
      

      If we run this code, we’ll receive the following output:

      Output

      ocean/animals/fish/shark.txt ocean/animals

      The output is similar to the earlier output, but now we’ve traversed yet another level higher by accessing .parent a second time. Two directories up from shark.txt is the ocean/animals directory.

      Using Glob to List Files

      It’s also possible to use the Path class to list files using the glob method.

      Let’s say we had a directory structure that looked like this:

      └── ocean
          ├── animals
          │   └── fish
          │       └── shark.txt
          ├── tides.txt
          └── wave.txt
      

      An ocean directory contains the files tides.txt and wave.txt. We have a file named shark.txt nested under the ocean directory, an animals directory, and a fish directory: ocean/animals/fish.

      To list all the .txt files in the ocean directory, we could say:

      for txt_path in Path("ocean").glob("*.txt"):
          print(txt_path)
      

      This code would yield output like:

      Output

      ocean/wave.txt ocean/tides.txt

      The "*.txt" glob pattern finds all files ending in .txt. Since the code sample executes that glob in the ocean directory, it returns the two .txt files in the ocean directory: wave.txt and tides.txt.

      Note: If you would like to duplicate the outputs shown in this example, you’ll need to mimic the directory structure illustrated here on your computer.

      We can also use the glob method recursively. To list all the .txt files in the ocean directory and all its subdirectories, we could say:

      for txt_path in Path("ocean").glob("**/*.txt"):
          print(txt_path)
      

      If we run this code, we’d receive output like the following:

      Output

      ocean/wave.txt ocean/tides.txt ocean/animals/fish/shark.txt

      The ** part of the glob pattern will match this directory and all directories beneath it, recursively. So, not only do we have the wave.txt and tides.txt files in the output, but we also receive the shark.txt file that was nested under ocean/animals/fish.

      Computing Relative Paths

      We can use the Path.relative_to method to compute paths relative to one another. The relative_to method is useful when, for example, you want to retrieve a portion of a long file path.

      Consider the following code:

      shark = Path("ocean", "animals", "fish", "shark.txt")
      below_ocean = shark.relative_to(Path("ocean"))
      below_animals = shark.relative_to(Path("ocean", "animals"))
      print(shark)
      print(below_ocean)
      print(below_animals)
      

      If we run this, we’ll receive output like the following:

      Output

      ocean/animals/fish/shark.txt animals/fish/shark.txt fish/shark.txt

      The relative_to method returns a new Path object relative to the given argument. In our example, we compute the Path to shark.txt relative to the ocean directory, and then relative to both the ocean and animals directories.

      If relative_to can’t compute an answer because we give it an unrelated path, it raises a ValueError:

      shark = Path("ocean", "animals", "fish", "shark.txt")
      shark.relative_to(Path("unrelated", "path"))
      

      We’ll receive a ValueError exception raised from this code that will be something like this:

      Output

      Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/Python3.8/pathlib.py", line 899, in relative_to raise ValueError("{!r} does not start with {!r}" ValueError: 'ocean/animals/fish/shark.txt' does not start with 'unrelated/path'

      unrelated/path is not a part of ocean/animals/fish/shark.txt, so there’s no way for Python to compute a relative path for us.

      Conclusion

      The pathlib module is a powerful part of the Python Standard Library that lets us manipulate filesystem paths quickly on any operating system. In this tutorial, we have learned to use some of pathlib’s key utilities for accessing file attributes, listing files with glob patterns, and traversing parent files and directories.

      The pathlib module exposes additional classes and utilities that we did not cover in this tutorial. Now that you have a baseline, you can use the pathlib module’s documentation to learn more about other available classes and utilities.

      If you’re interested in using other Python libraries, check out the following tutorials:



      Source link

      How To Use the sqlite3 Module in Python 3


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      SQLite is a self-contained, file-based SQL database. SQLite comes bundled with Python and can be used in any of your Python applications without having to install any additional software.

      In this tutorial, we’ll go through the sqlite3 module in Python 3. We’ll create a connection to a SQLite database, add a table to that database, insert data into that table, and read and modify data in that table.

      For this tutorial, we’ll be working primarily with an inventory of fish that we need to modify as fish are added to or removed from a fictional aquarium.

      Prerequisites

      To get the most out of this tutorial, it is recommended to have some familiarity with programming in Python and some basic background with SQL.

      You can review these tutorials for the necessary background information:

      Step 1 — Creating a Connection to a SQLite Database

      When we connect to a SQLite database, we are accessing data that ultimately resides in a file on our computer. SQLite databases are fully featured SQL engines that can be used for many purposes. For now, we’ll consider a database that tracks the inventory of fish at a fictional aquarium.

      We can connect to a SQLite database using the Python sqlite3 module:

      import sqlite3
      
      connection = sqlite3.connect("aquarium.db")
      

      import sqlite3 gives our Python program access to the sqlite3 module. The sqlite3.connect() function returns a Connection object that we will use to interact with the SQLite database held in the file aquarium.db. The aquarium.db file is created automatically by sqlite3.connect() if aquarium.db does not already exist on our computer.

      We can verify we successfully created our connection object by running:

      print(connection.total_changes)
      

      If we run this Python code, we will see output like:

      Output

      0

      connection.total_changes is the total number of database rows that have been changed by connection. Since we have not executed any SQL commands yet, 0 total_changes is correct.

      If, at any time, we find we want to start this tutorial again, we can delete the aquarium.db file from our computer.

      Note: It is also possible to connect to a SQLite database that resides strictly in memory (and not in a file) by passing the special string ":memory:" into sqlite3.connect(). For example, sqlite3.connect(":memory:"). A ":memory:" SQLite database will disappear as soon as your Python program exits. This might be convenient if you want a temporary sandbox to try something out in SQLite, and don’t need to persist any data after your program exits.

      Step 2 — Adding Data to the SQLite Database

      Now that we have connected to the aquarium.db SQLite database, we can start inserting and reading data from it.

      In a SQL database, data is stored in tables. Tables define a set of columns, and contain 0 or more rows with data for each of the defined columns.

      We will create a table named fish that tracks the following data:

      name species tank_number
      Sammy shark 1
      Jamie cuttlefish 7

      The fish table will track a value for name, species, and tank_number for each fish at the aquarium. Two example fish rows are listed: one row for a shark named Sammy, and one row for a cuttlefish named Jamie.

      We can create this fish table in SQLite using the connection we made in Step 1:

      cursor = connection.cursor()
      cursor.execute("CREATE TABLE fish (name TEXT, species TEXT, tank_number INTEGER)")
      

      connection.cursor() returns a Cursor object. Cursor objects allow us to send SQL statements to a SQLite database using cursor.execute(). The "CREATE TABLE fish ..." string is a SQL statement that creates a table named fish with the three columns described earlier: name of type TEXT, species of type TEXT, and tank_number of type INTEGER.

      Now that we have created a table, we can insert rows of data into it:

      cursor.execute("INSERT INTO fish VALUES ('Sammy', 'shark', 1)")
      cursor.execute("INSERT INTO fish VALUES ('Jamie', 'cuttlefish', 7)")
      

      We call cursor.execute() two times: once to insert a row for the shark Sammy in tank 1, and once to insert a row for the cuttlefish Jamie in tank 7. "INSERT INTO fish VALUES ..." is a SQL statement that allows us to add rows to a table.

      In the next section, we will use a SQL SELECT statement to inspect the rows we just inserted into our fish table.

      Step 3 — Reading Data from the SQLite Database

      In Step 2, we added two rows to a SQLite table named fish. We can retrieve those rows using a SELECT SQL statement:

      rows = cursor.execute("SELECT name, species, tank_number FROM fish").fetchall()
      print(rows)
      

      If we run this code, we will see output like the following:

      Output

      [('Sammy', 'shark', 1), ('Jamie', 'cuttlefish', 7)]

      The cursor.execute() function runs a SELECT statement to retrieve values for the name, species, and tank_number columns in the fish table. fetchall() retrieves all the results of the SELECT statement. When we print(rows) we see a list of two tuples. Each tuple has three entries; one entry for each column we selected from the fish table. The two tuples have the data we inserted in Step 2: one tuple for Sammy the shark, and one tuple for Jamie the cuttlefish.

      If we wanted to retrieve rows in the fish table that match a specific set of criteria, we can use a WHERE clause:

      target_fish_name = "Jamie"
      rows = cursor.execute(
          "SELECT name, species, tank_number FROM fish WHERE name = ?",
          (target_fish_name,),
      ).fetchall()
      print(rows)
      

      If we run this, we will see output like the following:

      Output

      [('Jamie', 'cuttlefish', 7)]

      As with the previous example, cursor.execute(<SQL statment>).fetchall() allows us to fetch all the results of a SELECT statement. The WHERE clause in the SELECT statement filters for rows where the value of name is target_fish_name. Notice that we use ? to substitute our target_fish_name variable into the SELECT statement. We expect to only match one row, and indeed we only see the row for Jamie the cuttlefish returned.

      Warning: Never use Python string operations to dynamically create a SQL statement string. Using Python string operations to assemble a SQL statement string leaves you vulnerable to SQL injection attacks. SQL injection attacks can be used to steal, alter, or otherwise modify data stored in your database. Always use the ? placeholder in your SQL statements to dynamically substitute values from your Python program. Pass a tuple of values as the second argument to Cursor.execute() to bind your values to the SQL statement. This substitution pattern is demonstrated here and in other parts of this tutorial as well.

      Step 4 — Modifying Data in the SQLite Database

      Rows in a SQLite database can be modified using UPDATE and DELETE SQL statements.

      Let’s say, for example, that Sammy the shark was moved to tank number 2. We can change Sammy’s row in the fish table to reflect this change:

      new_tank_number = 2
      moved_fish_name = "Sammy"
      cursor.execute(
          "UPDATE fish SET tank_number = ? WHERE name = ?",
          (new_tank_number, moved_fish_name)
      )
      

      We issue an UPDATE SQL statement to change the tank_number of Sammy to its new value of 2. The WHERE clause in the UPDATE statement ensures we only change the value of tank_number if a row has name = "Sammy".

      If we run the following SELECT statement, we can confirm our update was made correctly:

      rows = cursor.execute("SELECT name, species, tank_number FROM fish").fetchall()
      print(rows)
      

      If we run this, we will see output like the following:

      Output

      [('Sammy', 'shark', 2), ('Jamie', 'cuttlefish', 7)]

      Notice that the row for Sammy now has the value of 2 for its tank_number column.

      Let’s say that Sammy the shark was released into the wild and no longer held by the aquarium. Since Sammy no longer lives at the aquarium, it would make sense to remove the Sammy row from the fish table.

      Issue a DELETE SQL statement to remove a row:

      released_fish_name = "Sammy"
      cursor.execute(
          "DELETE FROM fish WHERE name = ?",
          (released_fish_name,)
      )
      

      We issue a DELETE SQL statement to remove the row for Sammy the shark. The WHERE clause in the DELETE statement ensures we only delete a row if that row has name = "Sammy".

      If we run the following SELECT statement, we can confirm our deletion was made correctly:

      rows = cursor.execute("SELECT name, species, tank_number FROM fish").fetchall()
      print(rows)
      

      If we run this code, we will see output like the following:

      Output

      [('Jamie', 'cuttlefish', 7)]

      Notice that the row for Sammy the shark is now gone, and only Jamie the cuttlefish remains.

      Step 5 — Using with Statements For Automatic Cleanup

      In this tutorial, we’ve used two primary objects to interact with the "aquarium.db" SQLite database: a Connection object named connection, and a Cursor object named cursor.

      In the same way that Python files should be closed when we are done working with them, Connection and Cursor objects should also be closed when they are no longer needed.

      We can use a with statement to help us automatically close Connection and Cursor objects:

      from contextlib import closing
      
      with closing(sqlite3.connect("aquarium.db")) as connection:
          with closing(connection.cursor()) as cursor:
              rows = cursor.execute("SELECT 1").fetchall()
              print(rows)
      

      closing is a convenience function provided by the contextlib module. When a with statement exits, closing ensures that close() is called on whatever object is passed to it. The closing function is used twice in this example. Once to ensure that the Connection object returned by sqlite3.connect() is automatically closed, and a second time to ensure that the Cursor object returned by connection.cursor() is automatically closed.

      If we run this code, we will see output like the following:

      Output

      [(1,)]

      Since "SELECT 1" is a SQL statement that always returns a single row with a single column with a value of 1, it makes sense to see a single tuple with 1 as its only value returned by our code.

      Conclusion

      The sqlite3 module is a powerful part of the Python standard library; it lets us work with a fully featured on-disk SQL database without installing any additional software.

      In this tutorial, we learned how to use the sqlite3 module to connect to a SQLite database, add data to that database, as well as read and modify data in that database. Along the way, we also learned about the risks of SQL injection attacks and how to use contextlib.closing to automatically call close() on Python objects in with statements.

      From here we can learn more about SQL databases in SQLite vs MySQL vs PostgreSQL: A Comparison Of Relational Database Management Systems.



      Source link

      Comment utiliser le module des collections dans Python 3


      L’auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      Python 3 possède un certain nombre de structures de données intégrées, notamment des tuples, des dictionnaires et des listes. Les structures de données nous fournissent un moyen d’organiser et de stocker les données. Le module de collections nous aide à remplir et à manipuler les structures de données de manière efficace.

      Dans ce tutoriel, nous allons passer en revue trois classes des collections pour vous aider à travailler avec des tuples, des dictionnaires et des listes. Nous utiliserons namedtuples pour créer des tuples avec des champs nommés, defaultdict pour regrouper de manière concise les informations dans les dictionnaires, et deque pour ajouter efficacement des éléments de chaque côté d’un objet de type liste. 

      Pour ce tutoriel, nous travaillerons principalement avec un inventaire de poissons que nous devons modifier au fur et à mesure que des poissons sont ajoutés ou retirés d’un aquarium fictif.

      Conditions préalables

      Pour tirer le meilleur parti de ce tutoriel, il est recommandé de se familiariser avec le tuple, le dictionnaire et les types de données de liste que ce soit en ce qui concerne leur syntaxe que de la manière d’en extraire des données. Vous pouvez consulter ces tutoriels pour obtenir les informations de base nécessaires :

      Ajouter des champs nominatifs aux tuples

      Les tuples de Python sont une séquence d’éléments immuables, ou inchangeables, ordonnés. Les tuples sont fréquemment utilisés pour représenter des données en colonnes ; par exemple, les lignes d’un fichier CSV ou les lignes d’une base de données SQL. Un aquarium peut garder une trace de son inventaire de poissons sous la forme d’une série de tuples.

      Un tuple de poissons individuel :

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

      Ce tuple est composé de trois éléments de chaîne caractères.

      Bien qu’utile à certains égards, ce tuple n’indique pas clairement ce que représente chacun de ses champs. En réalité, l’élément 0 est un nom, l’élément 1 est une espèce, et l’élément 2 est le réservoir de stockage. 

      Explication des champs de tuples de poissons :

      name species tank
      Sammy shark tank-a

      Ce tableau montre clairement que chacun des trois éléments du tuple a une signification claire.

      namedtuple du module collections vous permet d’ajouter des noms explicites à chaque élément d’un tuple pour rendre ces significations claires dans votre programme Python.

      Utilisons namedtuple pour générer une classe qui nomme clairement chaque élément du tuple de poisson :

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

      from collections import namedtuple donne à votre programme Python l’accès à la fonction d’usine namedtuple. L’appel de la fonction namedtuple() renvoie une classe qui est liée au nom Fish. Le namedtuple() a deux arguments : le nom souhaité de notre nouvelle classe "Fish" et une liste d’éléments nommés ["name", "species", "tank"]. 

      Nous pouvons utiliser la classe Fish pour représenter le tuple de poissons de tout à l’heure :

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

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

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

      sammy est instancié à l’aide de la classeFish. sammy est un tuple avec trois éléments clairement nommés. 

      Les champs de sammy sont accessibles par leur nom ou avec un index tuple traditionnel :

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

      Si nous lançons ces deux appels d’impression, nous obtiendrons le résultat suivant :

      Output

      shark shark

      L’accès à .species a la même valeur que l’accès au deuxième élément de sammy en utilisant [1]. 

      Utiliser namedtuple du module collections rend votre programme plus lisible tout en conservant les propriétés importantes d’un tuple (à savoir qu’ils sont immuables et ordonnés). 

      De plus, la fonction d’usine namedtuple ajoute plusieurs méthodes supplémentaires aux instances de Fish.

      Utilisez ._asdict() pour convertir une instance en dictionnaire :

      print(sammy._asdict())
      

      Si nous lançons print, vous verrez des résultats comme ceux qui suivent :

      Output

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

      Appeler .asdict() sur sammy renvoie un dictionnaire mettant en correspondance les noms de chacun des trois champs avec leurs valeurs correspondantes. 

      Les versions de Python plus anciennes que 3.8 pourraient produire cette ligne de manière légèrement différente. Vous pourriez, par exemple, voir un OrderedDict au lieu du simple dictionnaire présenté ici. 

      Note : Dans Python, les méthodes avec des traits de soulignement en tête sont généralement considérées comme « privées ». Les méthodes supplémentaires fournies par namedtuple (comme _asdict(), ._make(), ._replace(), etc.), sont toutefois publiques.

      Rassembler des données dans un dictionnaire

      Il est souvent utile de collecter des données dans les dictionnaires Python. defaulttdict du module de collections peut nous aider à rassembler les informations dans les dictionnaires de manière rapide et concise. 

      defaultdict ne soulève jamais une KeyError. Si une clé n’est pas présente, defaulttdict se contente d’insérer et de renvoyer une valeur de remplacement à la place : 

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

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

      []

      defaultdict insère et renvoie une valeur de remplacement au lieu de lancer une KeyError. Dans ce cas, nous avons spécifié la valeur de remplacement sous forme de liste.

      Les dictionnaires ordinaires, en revanche, lancent une KeyError sur les clés manquantes :

      my_regular_dict = {}
      
      my_regular_dict["missing"]
      

      Si nous exécutons ce code, nous obtiendrons le résultat suivant :

      Output

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

      Le dictionnaire habituel my_regular_dict soulève une KeyError lorsque nous essayons d’accéder à une clé qui n’est pas présente. 

      defaulttdict se comporte différemment d’un dictionnaire ordinaire. Au lieu de soulever une KeyError sur une clé manquante, defaultdict appelle la valeur de remplacement sans argument pour créer un nouvel objet. Dans ce cas, list() pour créer une liste vide. 

      Pour continuer avec notre exemple d’aquarium fictif, disons que nous avons une liste de tuples de poissons représentant l’inventaire d’un aquarium :

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

      Trois poissons existent dans l’aquarium – leur nom, leur espèce et leur bac de rétention sont notés dans ces trois tuples.

      Notre objectif est d’organiser notre inventaire par réservoir – nous voulons connaître la liste des poissons présents dans chaque réservoir. En d’autres termes, nous voulons un dictionnaire qui cartographie "tank-a". à ["Jamie", "Mary"] et "tank-b" à ["Jamie"].

      Nous pouvons utiliser defaulttdict pour regrouper les poissons par bassin :

      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)
      

      En exécutant ce code, nous obtiendrons le résultat suivant :

      Output

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

      fish_names_by_tank est déclaré comme un paramètre par défaut qui consiste par défaut à insérer list() au lieu de lancer une KeyError.  Comme cela garantit que chaque clé dansfish_names_by_tank pointera sur une liste, nous pouvons librement appeler .append() pour ajouter des noms à la liste de chaque réservoir.

      defaultdict vous aide ici car il réduit le risque d’erreurs inattendues de KeyErrors. Réduire lesKeyErrors inattendues signifie que votre programme peut être écrit plus clairement et avec moins de lignes. Plus concrètement, l’idiome defaultdict permet d’éviter d’instancier manuellement une liste vide pour chaque réservoir.

      Sans défaultdict, le corps de la boucle for aurait pu ressembler davantage à ceci :

      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)
      

      L’utilisation d’un simple dictionnaire (au lieu d’un defaultdict) signifie que le corps de la boucle for doit toujours vérifier l’existence du tank donné dans fish_names_by_tank. Ce n’est qu’après avoir vérifié que tank est déjà présent dans fish_names_by_tank, ou vient d’être initialisé avec un [], qu’on peut ajouter le nom du poisson.

      defaultdict peut aider à réduire le nombre de codes passe-partout lors du remplissage des dictionnaires car il ne provoque jamais de KeyError.

      Utilisation de deque pour ajouter efficacement des éléments de chaque côté d’une collection

      Les listes Python sont une séquence d’éléments ordonnée, mutable ou modifiable. Python peut ajouter des listes en temps constant (la longueur de la liste n’a aucun effet sur le temps qu’il faut pour l’ajout), mais l’insertion au début d’une liste peut être plus lente (le temps nécessaire augmente à mesure que la liste s’agrandit).

      En termes de notation Big O, l’ajout à une liste est une opération O(1) à temps constant. L’insertion au début d’une liste, en revanche, est plus lente avec O(n) performance. 

      Note : Les informaticiens mesurent souvent la performance des procédures en utilisant ce qu’on appelle la notation « Big O ». Lorsque la taille d’une entrée n’a aucun effet sur le temps nécessaire pour exécuter une procédure, on dit qu’elle se déroule en temps constant ou O(1) (“Big O of 1”). Comme vous l’avez appris plus haut, Python peut s’ajouter aux listes à performance temporelle constante, autrement dit O(1). 

      Parfois, la taille d’une entrée a une incidence directe sur le temps nécessaire à l’exécution d’une procédure. L’insertion au début d’une liste Python, par exemple, est d’autant plus lente qu’il y a plus d’éléments dans la liste. La notation Big O utilise la lettre n pour représenter la taille de l’entrée. Cela signifie que l’ajout d’éléments au début d’une liste Python se fait en « temps linéaire » ou O(n) (“Big O of n”). 

      En général, les procédures O(1) sont plus rapides que les procédures O(n).

      On peut l’insérer au début d’une liste Python :

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

      Si nous exécutons ce qui suit, nous obtiendrons le résultat suivant :

      Output

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

      La méthode .insert (index, objet) sur list nous permet d’insérer "Alice" au début de favorite_fish_list. Toutefois, il est à noter que l’insertion au début d’une liste a O(n) performance. Comme la durée de favorite_fish_list augmente, le temps nécessaire pour insérer un poisson au début de la liste augmentera proportionnellement et prendra de plus en plus de temps.

      deque (prononcé « deck ») du module collections est un objet de type liste qui nous permet d’insérer des éléments au début ou à la fin d’une séquence avec une performance à temps constant (O(1)).

      Insérez un élément au début d’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)
      

      En exécutant ce code, nous obtiendrons le résultat suivant :

      Output

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

      Nous pouvons instancier un deque en utilisant un ensemble d’éléments préexistants, en l’occurrence une liste de trois noms de poissons favoris. L’appel de la méthode appendleft de favorite_fish_deque nous permet d’insérer un élément au début de notre collection avec la performance O (1). O(1) performance signifie que le temps nécessaire pour ajouter un élément au début de favorite_fish_deque n’augmentera pas, même si favorite_fish_deque a des milliers ou des millions d’éléments. 

      Note : Bien que deque ajoute des entrées au début d’une séquence plus efficacement qu’une liste, deque n’effectue pas toutes ses opérations plus efficacement qu’une liste. Par exemple, l’accès à un article aléatoire dans un deque a une performance O(n), mais l’accès à un élément aléatoire d’une liste a une performance O(1). Utilisez deque lorsqu’il est important d’insérer ou de retirer rapidement des éléments de chaque côté de votre collection. Une comparaison complète des performances temporelles est disponible sur le wiki de Python.

      Conclusion

      Le module des collections est une partie puissante de la bibliothèque standard Python qui vous permet de travailler avec des données de manière concise et efficace.  Ce tutoriel couvrait trois des cours fournis par le module des collections comprenant namedtuple, defaultdict, et deque. 

      D’ici, vous pouvez utiliser la documentation du module de collecte pour en savoir plus sur les autres classes et utilités disponibles. Pour en savoir plus sur Python en général, vous pouvez lire notre Série de tutoriels Comment coder en Python 3. 



      Source link