FAQ sur la bibliothèque et les extensions
*****************************************


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 connaîtrez 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 *curses* 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.


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-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)

donc il doit être déclaré avec deux paramètres :

   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.

Une fois que la logique du programme est implémentée par un ensemble
de fonctions et de comportements de classes, il faut écrire des
fonctions de test qui vont éprouver cette logique. À chaque module, il
est possible d'associer une suite de tests qui joue de manière
automatique un ensemble de tests. Au premier abord, il semble qu'il
faille fournir un effort conséquent, mais comme Python est un langage
concis et flexible, c'est surprenamment aisé. Écrire simultanément le
code « de production » et les fonctions de test associées rend le
développement plus agréable et plus amusant, car ceci permet de
trouver des bogues, voire des défauts de conception, plus facilement.

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* ?
------------------------------------------------------------

The "pydoc" module can create HTML from the doc strings in your Python
source code.  An alternative for creating API documentation purely
from docstrings is epydoc.  Sphinx can also include docstring content.


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".


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 ?
-----------------------------------------------------------------------------------

La manière la plus simple est d'utiliser le module
"concurrent.futures", en particulier la classe "ThreadPoolExecutor".

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.current_thread(), end=' ')
               print('queue empty')
               break
           else:
               print('Worker', threading.current_thread(), 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 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)" où
*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 ?
---------------------------

The "shutil" module contains a "copyfile()" function. Note that on
Windows NTFS volumes, it does not copy alternate data streams nor
resource forks on macOS HFS+ volumes, though both are now rarely used.
It also doesn't copy file permissions and metadata, though using
"shutil.copy2()" instead will preserve most (though not all) of it.


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, OSX, Linux, BSD, Jython et IronPython :

   https://pypi.org/project/pyserial/

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

   https://groups.google.com/groups?selm=34A04430.CF9@ohioee.com


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 maintains a useful set of pages about Python web
technologies at https://web.archive.org/web/20210224183619/http://pha
seit.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 ?

Oui. Voici un exemple simple d'utilisation de "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 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:

  Guide pratique : récupérer des ressources web en utilisant le module
  urllib 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.

Pour empêcher une connexion TCP de se bloquer, il est possible de
mettre le connecteur en mode lecture seule. Avec cela, au moment du
"socket.connect()", la connexion pourra être immédiate (peu probable)
ou bien vous obtiendrez une exception qui contient le numéro d'erreur
dans ".errno". "errno.EINPROGRESS" indique que la connexion est en
cours, mais qu'elle n'a pas encore aboutie. La valeur dépend du
système d'exploitation, donc renseignez-vous sur la valeur utilisée
par votre système.

"socket.connect_ex()" permet d'éviter la création de l'exception, et
de ne renvoyer que la valeur de *errno*. Pour vérifier l'état de la
connexion, utilisez encore une fois "socket.connect_ex()" — "0" ou
"errno.EISCONN" indiquent que la connexion est active — ou passez le
connecteur en argument de "select.select()" pour vérifier si le
connecteur est prêt à recevoir des entrées.

Note:

  Le module "asyncore" propose une approche générique mono-processus
  asynchrone pour écrire du code réseau non-bloquant. La bibliothèque
  tierce Twisted en est une alternative plébiscitée, avec un grand
  nombre de fonctionnalités.


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 relationnelle 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)" sélectionne au hasard un élément d'une séquence donnée.

* "shuffle(L)" mélange une liste en-place, c.-à-d. lui applique une
  permutation aléatoire.

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