FAQ sur la bibliothèque et les extensions

Sommaire

Questions générales sur la bibliothèque

Comment trouver un module ou une application pour effectuer la tâche X ?

Regardez si la bibliothèque standard contient un module approprié (avec l'expérience, vous connaitrez le contenu de la bibliothèque standard et pourrez sauter cette étape).

Pour des paquets tiers, regardez dans l'index des paquets Python ou essayez Google ou un autre moteur de recherche (NDT : comme le moteur français Qwant). Rechercher « Python » accompagné d'un ou deux mots-clés se rapportant à ce qui vous intéresse donne souvent de bons résultats.

Où sont stockés les fichiers sources math.py, socket.py, regex.py, etc ?

Si vous ne parvenez pas à trouver le fichier source d'un module, c'est peut-être parce que celui-ci est un module natif ou bien un module implémenté en C, C++, ou autre langage compilé, qui est chargé dynamiquement. Dans ce cas, vous ne possédez peut-être pas le fichier source ou celui-ci est en réalité stocké quelque part dans un dossier de fichiers source C (qui ne sera pas dans le chemin Python), comme par exemple mathmodule.c.

Il y a (au moins) trois types de modules dans Python :

  1. les modules écrits en Python (.py) ;

  2. les modules écrits en C et chargés dynamiquement (.dll, .pyd, .so, .sl, etc.) ;

  3. les modules écrits en C et liés à l'interpréteur ; pour obtenir leur liste, entrez :

    import sys
    print(sys.builtin_module_names)
    

Comment rendre un script Python exécutable sous Unix ?

Deux conditions doivent être remplies : les droits d'accès au fichier doivent permettre son exécution et la première ligne du script doit commencer par #! suivi du chemin vers l'interpréteur Python.

La première condition est remplie en exécutant chmod +x scriptfile ou chmod 755 scriptfile.

Il y a plusieurs façons de remplir la seconde. La plus simple consiste à écrire au tout début du fichier

#!/usr/local/bin/python

en utilisant le chemin de l'interpréteur Python sur votre machine.

Pour rendre ce script indépendant de la localisation de l'interpréteur Python, il faut utiliser le programme env. La ligne ci-dessous fonctionne sur la quasi-totalité des dérivés de Unix, à condition que l'interpréteur Python soit dans un dossier référencé dans la variable PATH de l'utilisateur :

#!/usr/bin/env python

Ne faites pas ceci pour des scripts CGI. La variable PATH des scripts CGI est souvent très succincte, il faut par conséquent préciser le chemin absolu réel de l'interpréteur.

Il peut arriver que l'environnement d'un utilisateur soit si chargé que le programme /usr/bin/env échoue ; ou que le programme env n'existe pas du tout. Dans ce cas, vous pouvez utiliser l'astuce suivante, élaborée par Alex Rezinsky :

#! /bin/sh
""":"
exec python $0 ${1+"$@"}
"""

Le léger inconvénient est que cela définit la variable __doc__ du script. Cependant, il est possible de corriger cela en ajoutant

__doc__ = """...Whatever..."""

Existe-t'il un module curse ou termcap en Python ?

Pour les dérivés d'Unix : la distribution standard de Python contient un module curses dans le sous-dossier Modules, bien qu'il ne soit pas compilé par défaut. Il n'est pas disponible en Windows — le module curses n'existant pas en Windows.

Le module curses comprend les fonctionnalités de base de curses et beaucoup de fonctionnalités supplémentaires provenant de ncurses et de SYSV curses comme la couleur, la gestion des ensembles de caractères alternatifs, la prise en charge du pavé tactile et de la souris. Cela implique que le module n'est pas compatible avec des systèmes d'exploitation qui n'ont que le curses de BSD mais, de nos jours, de tels systèmes d'exploitation ne semblent plus exister ou être maintenus.

Pour Windows : utilisez le module consolelib.

Existe-t'il un équivalent à la fonction C onexit() en Python ?

Le module atexit fournit une fonction d'enregistrement similaire à la fonction C onexit().

Pourquoi mes gestionnaires de signaux ne fonctionnent-t'ils pas ?

Le problème le plus courant est d'appeler le gestionnaire de signaux avec les mauvais arguments. Un gestionnaire est appelé de la façon suivante

handler(signum, frame)

so it should be declared with two parameters:

def handler(signum, frame):
    ...

Tâches fréquentes

Comment tester un programme ou un composant Python ?

Python fournit deux cadriciels de test. Le module doctest cherche des exemples dans les docstrings d'un module et les exécute. Il compare alors la sortie avec la sortie attendue, telle que définie dans la docstring.

Le module unittest est un cadriciel un peu plus élaboré basé sur les cadriciels de test de Java et de Smalltalk.

Pour rendre le test plus aisé, il est nécessaire de bien découper le code d'un programme. Votre programme doit avoir la quasi-totalité des fonctionnalités dans des fonctions ou des classes — et ceci a parfois l'avantage aussi plaisant qu'inattendu de rendre le programme plus rapide, les accès aux variables locales étant en effet plus rapides que les accès aux variables globales. De plus le programme doit éviter au maximum de manipuler des variables globales, car ceci rend le test beaucoup plus difficile.

La « logique générale » d'un programme devrait être aussi simple que

if __name__ == "__main__":
    main_logic()

à la fin du module principal du programme.

Once your program is organized as a tractable collection of function and class behaviours, you should write test functions that exercise the behaviours. A test suite that automates a sequence of tests can be associated with each module. This sounds like a lot of work, but since Python is so terse and flexible it's surprisingly easy. You can make coding much more pleasant and fun by writing your test functions in parallel with the "production code", since this makes it easy to find bugs and even design flaws earlier.

Les « modules auxiliaires » qui n'ont pas vocation à être le module principal du programme peuvent inclure un test pour se vérifier eux-mêmes.

if __name__ == "__main__":
    self_test()

Les programmes qui interagissent avec des interfaces externes complexes peuvent être testés même quand ces interfaces ne sont pas disponibles, en utilisant des interfaces « simulacres » implémentées en Python.

Comment générer la documentation à partir des docstrings ?

Le module pydoc peut générer du HTML à partir des docstrings du code source Python. Il est aussi possible de documenter une API uniquement à partir des docstrings à l'aide de epydoc. Sphinx peut également inclure du contenu provenant de docstrings.

Comment détecter qu'une touche est pressée ?

Pour les dérivés d'Unix, plusieurs solutions s'offrent à vous. C'est facile en utilisant le module curses, mais curses est un module assez conséquent à apprendre.

Fils d'exécution

Comment programmer avec des fils d'exécution ?

Veillez à bien utiliser le module threading et non le module _thread. Le module threading fournit une abstraction plus facile à manipuler que les primitives de bas-niveau du module _thread.

Un ensemble de diapositives issues du didacticiel de Aahz sur les fils d'exécution est disponible à http://www.pythoncraft.com/OSCON2001/.

Aucun de mes fils ne semble s'exécuter : pourquoi ?

Dès que le fil d'exécution principal se termine, tous les fils sont tués. Le fil principal s'exécute trop rapidement, sans laisser le temps aux autres fils de faire quoi que ce soit.

Une correction simple consiste à ajouter un temps d'attente suffisamment long à la fin du programme pour que tous les fils puissent se terminer :

import threading, time

def thread_task(name, n):
    for i in range(n):
        print(name, i)

for i in range(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10)  # <---------------------------!

Mais à présent, sur beaucoup de plates-formes, les fils ne s'exécutent pas en parallèle, mais semblent s'exécuter de manière séquentielle, l'un après l'autre ! En réalité, l'ordonnanceur de fils du système d'exploitation ne démarre pas de nouveau fil avant que le précédent ne soit bloqué.

Une correction simple consiste à ajouter un petit temps d'attente au début de la fonction :

def thread_task(name, n):
    time.sleep(0.001)  # <--------------------!
    for i in range(n):
        print(name, i)

for i in range(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10)

Au lieu d'essayer de trouver une bonne valeur d'attente pour la fonction time.sleep(), il vaut mieux utiliser un mécanisme basé sur les sémaphores. Une solution consiste à utiliser le module queue pour créer un objet file, faire en sorte que chaque fil ajoute un jeton à la file quand il se termine, et que le fil principal retire autant de jetons de la file qu'il y a de fils.

Comment découper et répartir une tâche au sein d'un ensemble de fils d'exécutions ?

The easiest way is to use the concurrent.futures module, especially the ThreadPoolExecutor class.

Ou bien, si vous désirez contrôler plus finement l'algorithme de distribution, vous pouvez écrire votre propre logique « à la main ». Utilisez le module queue pour créer une file de tâches ; la classe Queue gère une liste d'objets et a une méthode .put(objet) pour ajouter un élément à la file, et une méthode .get() pour les récupérer. La classe s'occupe de gérer les verrous pour que chaque tâche soit exécutée une et une seule fois.

Voici un exemple trivial :

import threading, queue, time

# The worker thread gets jobs off the queue.  When the queue is empty, it
# assumes there will be no more work and exits.
# (Realistically workers will run until terminated.)
def worker():
    print('Running worker')
    time.sleep(0.1)
    while True:
        try:
            arg = q.get(block=False)
        except queue.Empty:
            print('Worker', threading.currentThread(), end=' ')
            print('queue empty')
            break
        else:
            print('Worker', threading.currentThread(), end=' ')
            print('running with argument', arg)
            time.sleep(0.5)

# Create queue
q = queue.Queue()

# Start a pool of 5 workers
for i in range(5):
    t = threading.Thread(target=worker, name='worker %i' % (i+1))
    t.start()

# Begin adding work to the queue
for i in range(50):
    q.put(i)

# Give threads time to run
print('Main thread sleeping')
time.sleep(5)

Quand celui-ci est exécuté, il produit la sortie suivante :

Running worker
Running worker
Running worker
Running worker
Running worker
Main thread sleeping
Worker <Thread(worker 1, started 130283832797456)> running with argument 0
Worker <Thread(worker 2, started 130283824404752)> running with argument 1
Worker <Thread(worker 3, started 130283816012048)> running with argument 2
Worker <Thread(worker 4, started 130283807619344)> running with argument 3
Worker <Thread(worker 5, started 130283799226640)> running with argument 4
Worker <Thread(worker 1, started 130283832797456)> running with argument 5
...

Consultez la documentation du module pour plus de détails ; la classe Queue fournit une interface pleine de fonctionnalités.

Quels types de mutations sur des variables globales sont compatibles avec les programmes à fils d'exécution multiples ? sécurisé ?

Le verrou global de l'interpréteur (GIL pour global interpreter lock) est utilisé en interne pour s'assurer que la machine virtuelle Python (MVP) n'exécute qu'un seul fil à la fois. De manière générale, Python ne change de fil qu'entre les instructions du code intermédiaire ; sys.setswitchinterval() permet de contrôler la fréquence de bascule entre les fils. Chaque instruction du code intermédiaire, et, par conséquent, tout le code C appelé par cette instruction est donc atomique du point de vue d'un programme Python.

En théorie, cela veut dire qu'un décompte exact nécessite une connaissance parfaite de l'implémentation de la MVP. En pratique, cela veut dire que les opérations sur des variables partagées de type natif (les entier, les listes, les dictionnaires etc.) qui « semblent atomiques » le sont réellement.

Par exemple, les opérations suivantes sont toutes atomiques (L, L1 et L2 sont des listes, D, D1 et D2 sont des dictionnaires, x et y sont des objets, i et j des entiers) :

L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()

Les suivantes ne le sont pas :

i = i+1
L.append(L[-1])
L[i] = L[j]
D[x] = D[x] + 1

Les opérations qui remplacent d'autres objets peuvent invoquer la méthode __del__() de ces objets quand leur compteur de référence passe à zéro, et cela peut avoir de l'impact. C'est tout particulièrement vrai pour les des changements massifs sur des dictionnaires ou des listes. En cas de doute, il vaut mieux utiliser un mutex.

Pourquoi ne pas se débarrasser du verrou global de l'interpréteur ?

Le verrou global de l'interpréteur (GIL) est souvent vu comme un obstacle au déploiement de code Python sur des serveurs puissants avec de nombreux processeurs, car un programme Python à fils d'exécutions multiples n'utilise en réalité qu'un seul processeur. Presque tout le code Python ne peut en effet être exécuté qu'avec le GIL acquis.

À l'époque de Python 1.5, Greg Stein a bien implémenté un ensemble complet de correctifs (les correctifs « fils d'exécution libres ») qui remplaçaient le GIL par des verrous plus granulaires. Adam Olsen a conduit une expérience similaire dans son projet python-safethread. Malheureusement, ces deux tentatives ont induit une baisse significative (d'au moins 30%) des performances du code à un seul fil d'exécution, à cause de la quantité de verrouillage plus granulaire nécessaire pour contrebalancer la suppression du GIL.

Cela ne signifie pas qu'il est impossible de tirer profit de Python sur des machines à plusieurs cœurs ! Il faut seulement être malin et diviser le travail à faire entre plusieurs processus plutôt qu'entre plusieurs fils d'exécution. La classe ProcessPoolExecutor du nouveau module concurrent.futures permet de faire cela facilement ; le module multiprocessing fournit une API de plus bas-niveau pour un meilleur contrôle sur la distribution des tâches.

Des extensions C appropriées peuvent aussi aider ; en utilisant une extension C pour effectuer une tâche longue, l'extension peut relâcher le GIL pendant que le fil est en train d'exécuter ce code et laisser les autres fils travailler. Des modules de la bibliothèque standard comme zlib ou hashlib utilisent cette technique.

On a déjà proposé de restreindre le GIL par interpréteur, et non plus d'être complétement global ; les interpréteurs ne seraient plus en mesure de partager des objets. Malheureusement, cela n'a pas beaucoup de chance non plus d'arriver. Cela nécessiterait un travail considérable, car la façon dont beaucoup d'objets sont implémentés rend leur état global. Par exemple, les entiers et les chaînes de caractères courts sont mis en cache ; ces caches devraient être déplacés au niveau de l'interpréteur. D'autres objets ont leur propre liste de suppression, ces listes devraient être déplacées au niveau de l'interpréteur et ainsi de suite.

C'est une tâche sans fin, car les extensions tierces ont le même problème, et il est probable que les extensions tierces soient développées plus vite qu'il ne soit possible de les corriger pour les faire stocker leur état au niveau de l'interpréteur et non plus au niveau global.

Et enfin, quel intérêt y-a t'il à avoir plusieurs interpréteurs qui ne partagent pas d'état, par rapport à faire tourner chaque interpréteur dans un processus différent ?

Les entrées/sorties

Comment supprimer un fichier ? (et autres questions sur les fichiers…)

Utilisez os.remove(filename) ou os.unlink(filename) ; pour la documentation, référez-vous au module os. Ces deux fonctions sont identiques, unlink() n'est tout simplement que le nom de l'appel système à cette fonction sous Unix.

Utilisez os.rmdir() pour supprimer un dossier et os.mkdir() pour en créer un nouveau. os.makedirs(chemin) crée les dossiers intermédiaires de chemin qui n'existent pas et os.removedirs(chemin) supprime les dossiers intermédiaires si ceux-ci sont vides. Pour supprimer une arborescence et tout son contenu, utilisez shutil.rmtree().

os.rename(ancien_chemin, nouveau_chemin) permet de renommer un fichier.

Pour supprimer le contenu d'un fichier, ouvrez celui-ci avec f = open(nom_du_fichier, "rb+"), puis exécutez f.truncate(décalage)décalage est par défaut la position actuelle de la tête de lecture. Il existe aussi os.ftruncate(df, décalage) pour les fichiers ouverts avec os.open(), où df est le descripteur de fichier (un entier court).

Le module shutil propose aussi un grand nombre de fonctions pour effectuer des opérations sur des fichiers comme copyfile(), copytree() et rmtree().

Comment copier un fichier ?

Le module shutil fournit la fonction copyfile(). Sous MacOS 9, celle-ci ne copie pas le clonage de ressources ni les informations du chercheur.

Comment lire (ou écrire) des données binaires ?

Pour lire ou écrire des formats de données complexes en binaire, il est recommandé d'utiliser le module struct. Celui-ci permet de convertir une chaîne de caractères qui contient des données binaires, souvent des nombres, en objets Python, et vice-versa.

Par exemple, le code suivant lit, depuis un fichier, deux entiers codés sur 2 octets et un entier codé sur 4 octets, en format gros-boutiste :

import struct

with open(filename, "rb") as f:
    s = f.read(8)
    x, y, z = struct.unpack(">hhl", s)

« > » dans la chaîne de formatage indique que la donnée doit être lue en mode gros-boutiste, la lettre « h » indique un entier court (2 octets) et la lettre « l » indique un entier long (4 octets).

Pour une donnée plus régulière (p. ex. une liste homogène d'entiers ou de nombres à virgule flottante), il est possible d'utiliser le module array.

Note

Pour lire et écrire de la donnée binaire, il est obligatoire d'ouvrir le fichier en mode binaire également (ici, en passant "rb" à open()). En utilisant "r" (valeur par défaut), le fichier est ouvert en mode textuel et f.read() renvoie des objets str au lieu d'objets bytes.

Il me semble impossible d'utiliser os.read() sur un tube créé avec os.popen() ; pourquoi ?

os.read() est une fonction de bas niveau qui prend en paramètre un descripteur de fichier — un entier court qui représente le fichier ouvert. os.popen() crée un objet fichier de haut niveau, du même type que celui renvoyé par la fonction native open(). Par conséquent, pour lire n octets d'un tube p créé avec os.popen(), il faut utiliser p.read(n).

Comment accéder au port de transmission en série (RS-232) ?

Pour Win32, POSIX (Linux, BSD, etc.) et Jython :

Pour Unix, référez-vous à une publication sur Usenet de Mitch Chapman :

Pourquoi fermer sys.stdout, sys.stdin, sys.stderr ne les ferme pas réellement ?

Les objets fichiers en Python sont des abstractions de haut niveau sur les descripteurs de fichier C de bas niveau.

Pour la plupart des objets fichiers créés en Python avec la fonction native open(), f.close() marque le fichier comme fermé du point de vue de Python et ferme aussi le descripteur C sous-jacent. Le même mécanisme est enclenché automatiquement dans le destructeur de f, lorsque f est recyclé.

Mais stdin, stdout et stderr ont droit à un traitement spécial en Python, car leur statut en C est lui-aussi spécial. Exécuter sys.stdout.close() marque l'objet fichier comme fermé du point de vue de Python, mais le descripteur de fichier C associé n'est pas fermé.

Pour fermer le descripteur de fichier sous-jacent C de l'une de ces trois sorties, demandez-vous avant tout si vous êtes sûr de vous (cela peut, par exemple, perturber le bon fonctionnement de modules qui font des opérations d'entrée-sortie). Si c'est le cas, utilisez os.close() :

os.close(stdin.fileno())
os.close(stdout.fileno())
os.close(stderr.fileno())

Il est aussi possible de fermer respectivement les constantes numériques 0, 1 ou 2.

Programmation réseau et Internet

Quels sont les outils Python dédiés à la Toile ?

Référez-vous aux chapitres intitulés Gestion des protocoles internet et Traitement des données provenant d'Internet dans le manuel de référence de la bibliothèque. Python a de nombreux modules pour construire des applications de Toile côté client comme côté serveur.

Un résumé des cadriciels disponibles est maintenu par Paul Boddie à l'adresse https://wiki.python.org/moin/WebProgramming.

Cameron Laird maintient un ensemble intéressant d'articles sur les technologies Python dédiées à la Toile à l'adresse http://phaseit.net/claird/comp.lang.python/web_python.

Comment reproduire un envoi de formulaire CGI (METHOD=POST) ?

J'aimerais récupérer la page de retour d'un envoi de formulaire sur la Toile. Existe-t'il déjà du code qui pourrait m'aider à le faire facilement ?

Yes. Here's a simple example that uses urllib.request:

#!/usr/local/bin/python

import urllib.request

# build the query string
qs = "First=Josephine&MI=Q&Last=Public"

# connect and send the server a path
req = urllib.request.urlopen('http://www.some-server.out-there'
                             '/cgi-bin/some-cgi-script', data=qs)
with req:
    msg, hdrs = req.read(), req.info()

Remarquez qu'en général, la chaîne de caractères d'une requête POST encodée avec des des signes « % » doit être mise entre guillemets à l'aide de urllib.parse.urlencode(). Par exemple pour envoyer name=Guy Steele, Jr. :

>>> import urllib.parse
>>> urllib.parse.urlencode({'name': 'Guy Steele, Jr.'})
'name=Guy+Steele%2C+Jr.'

Voir aussi

HOWTO Fetch Internet Resources Using The urllib Package pour des exemples complets.

Quel module utiliser pour générer du HTML ?

La page wiki de la programmation Toile (en anglais) répertorie un ensemble de liens pertinents.

Comment envoyer un courriel avec un script Python ?

Utilisez le module smtplib de la bibliothèque standard.

Voici un exemple très simple d'un envoyeur de courriel qui l'utilise. Cette méthode fonctionne sur tous les serveurs qui implémentent SMTP.

import sys, smtplib

fromaddr = input("From: ")
toaddrs  = input("To: ").split(',')
print("Enter message, end with ^D:")
msg = ''
while True:
    line = sys.stdin.readline()
    if not line:
        break
    msg += line

# The actual mail send
server = smtplib.SMTP('localhost')
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

Sous Unix, il est possible d'utiliser sendmail. La localisation de l'exécutable sendmail dépend du système ; cela peut-être /usr/lib/sendmail ou /usr/sbin/sendmail, la page de manuel de sendmail peut vous aider. Par exemple :

import os

SENDMAIL = "/usr/sbin/sendmail"  # sendmail location
p = os.popen("%s -t -i" % SENDMAIL, "w")
p.write("To: receiver@example.com\n")
p.write("Subject: test\n")
p.write("\n")  # blank line separating headers from body
p.write("Some text\n")
p.write("some more text\n")
sts = p.close()
if sts != 0:
    print("Sendmail exit status", sts)

Comment éviter de bloquer dans la méthode connect() d'un connecteur réseau ?

Le module select est fréquemment utilisé pour effectuer des entrées-sorties asynchrones sur des connecteurs réseaux.

To prevent the TCP connect from blocking, you can set the socket to non-blocking mode. Then when you do the socket.connect(), you will either connect immediately (unlikely) or get an exception that contains the error number as .errno. errno.EINPROGRESS indicates that the connection is in progress, but hasn't finished yet. Different OSes will return different values, so you're going to have to check what's returned on your system.

You can use the socket.connect_ex() method to avoid creating an exception. It will just return the errno value. To poll, you can call socket.connect_ex() again later -- 0 or errno.EISCONN indicate that you're connected -- or you can pass this socket to select.select() to check if it's writable.

Note

The asyncio module provides a general purpose single-threaded and concurrent asynchronous library, which can be used for writing non-blocking network code. The third-party Twisted library is a popular and feature-rich alternative.

Bases de données

Existe-t'il des modules Python pour s'interfacer avec des bases de données ?

Oui.

La distribution standard de Python fournit aussi des interfaces à des bases de données comme DBM ou GDBM. Il existe aussi le module sqlite3 qui implémente une base de données relationelle légère sur disque.

La gestion de la plupart des bases de données relationnelles est assurée. Voir la page wiki DatabaseProgramming pour plus de détails.

Comment implémenter la persistance d'objets en Python ?

Le module pickle répond à cela de manière large (bien qu'il ne soit pas possible de stocker des fichiers ouverts, des connecteurs ou des fenêtres par exemple), et le module shelve de la bibliothèque utilise pickle et (g)dbm pour créer des liens persistants vers des objets Python.

Mathématiques et calcul numérique

Comment générer des nombres aléatoires en Python ?

Le module random de la bibliothèque standard comprend un générateur de nombres aléatoires. Son utilisation est simple :

import random
random.random()

Le code précédent renvoie un nombre à virgule flottante aléatoire dans l'intervalle [0, 1[.

Ce module fournit beaucoup d'autres générateurs spécialisés comme :

  • randrange(a, b) génère un entier dans l'intervalle [a, b[.

  • uniform(a, b) génère un nombre à virgule flottante aléatoire dans l'intervalle [a, b[.

  • normalvariate(mean, sdev) simule la loi normale (Gaussienne).

Des fonctions de haut niveau opèrent directement sur des séquences comme :

  • choice(S) chooses a random element from a given sequence.

  • shuffle(L) shuffles a list in-place, i.e. permutes it randomly.

Il existe aussi une classe Random qu'il est possible d'instancier pour créer des générateurs aléatoires indépendants.