Looking for Computer Science  & Information Technology online courses ?
Check my new web site: https://www.yesik.it !

Comme la plupart des langages scripts, Python n'a pas vraiment la notion de programme principal: les instructions qui ne sont pas incluses dans une fonction utilisateur sont simplement exécutées dans l'ordre où elles apparaissent dans le fichier:

#
# bad-style.py
#
 
print "Bonjour"
 
def someFunction():
    doThis();
    doThat();
 
print "Bienvenue sur Python"

Le programme ci-dessus est de style plutôt douteux, puisque j'y mélange instructions directement exécutables (c'est à dire en dehors de toute fonction) et définition de fonction. Néanmoins, c'est un programme Python parfaitement valide. Et qui peut être exécuté::

sh$  python bad-style.py
Bonjour
Bienvenue sur Python

Les deux print sont bien exécutés, puisqu'il sont en dehors de toute fonction – mais pas les instructions de la fonction someFunction puisque celle-ci n'est jamais appelée.

Comme je disais en préambule, c'est là le fonctionnement traditionnel des langages de script. Alors, pourquoi en parler? Parce qu'un fichier source Python peut être utilisé de deux manières différentes: Pour l'exécuter, comme nous venons de le voir. Mais aussi pour l'importer dans un autre programme.

Le problème étant que les instructions hors fonction sont exécutées aussi bien quand le programme est exécuté que quand il est importé. Ce qui n'est pas forcément souhaitable.

Un exemple

#
# compte.py
#
import unittest
 
class CompteNonApprovisionne(Exception):
    """Pas assez d'argent sur le compte..."""
 
class Compte:
    """Un compte bancaire"""
    def __init__(self):
	self.solde = 0
    def credite(self, amount):
	self.solde += amount
    def debite(self, amount):
	if self.solde < amount:
	    raise CompteNonApprovisionne()
	self.solde -= amount
 
class TestCompte(unittest.TestCase):
    def testDebit(self):
	compte = Compte()
	compte.credite(100)
	self.assertRaises(CompteNonApprovisionne, compte.debite, 200)
	self.assertEqual(compte.solde, 100)
 
    def testCreationCompte(self):
	compte = Compte()
	self.assertEqual(compte.solde, 0)
 
unittest.main()

Ainsi, le programme ci-dessus est un exemple typique: il définit une classe Compte, vraisemblablement destinée à être utilisée dans un programme plus vaste. Mais ce code source contient également le code de test qui permet de s'assurer du fonctionnement correct de la classe Compte. Et les tests sont exécutés par le framework de tests unitaires de Python lors de l'exécution de l'instruction suivante:

unittest.main()

Comme cette instruction est en dehors de toute définition de fonction, elle est donc exécutée à chaque fois que le programme est chargé par l'interpréteur.

On peut d'ailleurs s'en assurer facilement:

sh$  python compte.py 
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Mais que va-t-il se passer quand ce fichier va être importé comme un module?

#
# banque.py
#
from compte import Compte
 
#...

Et là, surprise: même si c'est via un import, le programme Compte.py est bel et bien chargé par l'interpréteur. Et donc, les instructions hors fonction qu'il contient sont exécutées. On peut le vérifier en exécutant le programme banque.py:

sh$ python banque.py 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Oups! L'importation de compte.py a déclenché l'exécution des instructions hors fonction de ce fichier. Ce comportement peut être souhaitable par exemple dans le cas d'instructions destinées à initialiser le module. Par contre, dans notre exemple, il s'agit de tests unitaires. Il n'est donc pas souhaitable qu'ils soient effectués à chaque chargement du module.

La solution

Heureusement, il y a une solution: En effet, Python dispose d'une variable globale __name__ qui contient soit le nom du module quand le fichier est chargé comme un module, soit la chaîne "__main__" si le programme est utilisé de manière autonome. Par conséquent, il est possible de tester cette variable pour sélectionner le code qui ne doit être effectué que quand le fichier est exécuté en tant que programme. C'est un idiome Python que l'on retrouve fréquemment:

if __name__ == "__main__":
    unittest.main()