On appelle namespace ou espace de nommage en français, un mĂ©canisme en Python qui isole les identifiants dans des espaces diffĂ©rents. Ils sont utiles pour aider Ă l'organisation du code et des identifiants en unitĂ©s logiques plus facile Ă gĂ©rer car isolĂ©es les unes des autres, plutĂŽt que de mettre tous les identifiants dans un seul et unique sac, cela serait vite ingĂ©rable. Par exemple dans un namespace A on peut avoir les identifiants a, b et c, qui ne seront pas les mĂȘmes que les identifiants a, b et dans un namespace B.
On définit des namespaces à chaque fois qu'on crée une fonction, une classe ou un module. Chaque instance des classes, donc chaque objet, sont aussi des namespaces. Pour les fonctions, l'espace de nommage qu'une fonction définit est créé à chaque fois que la fonction démarre son exécution, et détruit à chaque fois qu'elle se termine. Les classes quant à elles ont leur espace de nommage créé dÚs que le module dans lequel elles existent est importé. (Plus techniquement les namespaces sont liés à la notion d'objet, car chaque namespace est en fait un objet, et chaque objet est un namespace, les deux notions sont liées, une instance et les identifiants dans un namespace sont les attributs de cet objet)
Jusquâici on a créé des programmes Python dans un seul fichier .py. Au fur et Ă mesure quâun programme devient long et complexe, le fichier
devient difficile à naviguer et à lire, à ce moment on sépare le code dans plusieurs fichiers .py. Le mécanisme de Python qui permet de
faire cela est celui des modules avec l'importation. On appelle module un fichier .py tout
simplement. On appelle importation le fait de rendre disponible le contenu d'un module depuis un autre.
Quels morceaux du code vont dans quels fichiers sont au choix des dĂ©veloppeurs, ainsi que le nombre et le nom de ces fichiers. Python offre la possibilitĂ© de le faire, mais nous sommes libres dâutiliser cette fonctionnalitĂ© comme nous lâentendons. En gĂ©nĂ©ral, on place le code liĂ© Ă une mĂȘme fonctionnalitĂ©, ou un mĂȘme aspect du programme dans un mĂȘme fichier, qui doit ĂȘtre le plus isolĂ© et indĂ©pendant possible des autres fichiers et donc du reste du code.
On appelle aussi les modules bibliothĂšques, ou librairies. Elles peuvent ĂȘtre dans un seul fichier, ou bien dans un ensemble de fichiers cohĂ©rents dans le cas de grosses bibliothĂšques. On appelle packages un ensemble de modules dans un mĂȘme dossier, ils sont un moyen de structurer les modules. mais on ne les verra pas dans ce cours. Ils sont cependant un aspect important de Python Ă Ă©tudier plus tard.
Quand une bibliothĂšque comporte plusieurs fichiers, ils peuvent ĂȘtre inter-dĂ©pendants, câest Ă dire quâils se rĂ©fĂ©rencent les uns les autres, mais idĂ©alement le moins possible. En revanche, ils doivent ĂȘtre le plus indĂ©pendants possible de tout code hors de la bibliothĂšque, idĂ©alement 100% indĂ©pendants de l'extĂ©rieur.
Le gros avantage lorsquâon met du code dans des bibliothĂšques sous forme de modules ou packages, est quâon peut le rĂ©utiliser oĂč on veut, depuis nâimporte quel autre fichier Python. On peut donc rĂ©utiliser des fonctions ou des classes, et Ă©viter de devoir les rĂ©-Ă©crire ou les copier-coller dans plusieurs fichiers.
Voyons un exemple de modules: soit les deux modules suivants dans un mĂȘme dossier:
Un dossier/
|-- modA.py
|-- modB.py
# Ceci est le fichier modA.py, c'est donc le module modA
variable = 'Bonjour modA'
def f():
print("Une fonction dans modA")
Avec l'instruction import on peut importer un module dans un autre, pour avoir ensuite accÚs à ses variables et fonctions, en préfixant leur
nom par celui du module suivi d'un point, comme par exemple modA.variable.
# Ceci est le fichier modB.py, c'est donc le module modB
# On importe modA, remarquez qu'on ne met pas .py Ă la fin
import modA
variable = 'Bonjour modB'
# â Affichera 'Bonjour modB'
print(variable)
# â Affichera 'Bonjour modA'
print(modA.variable)
# â Affichera 'Une fonction dans modA'
modA.f()
GrĂące Ă la notion de namespaces les deux modules peuvent contenir des identifiants ayant le mĂȘme nom (variable ou f), et
pourtant ils ne sont pas les mĂȘmes, ils rĂ©fĂ©rencent des objets diffĂ©rents.
(schéma des deux modules modA et modB et leurs identifiants)
importAprĂšs qu'un module est importĂ©, un identifiant qui porte son nom est créé dans le namespace oĂč l'instruction import se situe (dans l'exemple
ci-dessous le nom "modA" est créé par le "import"). Cet identifiant pointe vers un objet nouvellement créé de type module correspondant au
module importé, objet par lequel on accÚdera au module importé (le module modA.py dans l'exemple ci-dessous). C'est donc par cet objet qu'on peut
accéder au namespace du module que l'objet représente, et donc à tous les noms présents dans ce module.
# Ceci est le fichier modB.py
import modA
print(modA)
print(type(modA))
Executons modB.py:
<module 'modA' from 'C:\\Users\\Arnaud\\Desktop\\modA.py'>
<class 'module'>
On place gĂ©nĂ©ralement les instructions import tout en haut des fichiers, mais ils peuvent apparaĂźtre n'importe oĂč.
Attention à ne pas créer de boucles dans les importations, c'est à dire deux modules ou plus qui s'importent mutuellement:
# Ceci est le fichier modA.py
import modB # <= ERREUR: boucle avec import dans modB
# Ceci est le fichier modB.py
import modA # <= ERREUR: boucle avec import dans modA
La premiĂšre fois qu'un module est importĂ©, tout son contenu est exĂ©cutĂ© une fois. Pour cette raison importer plusieurs fois le mĂȘme module, que ça soit depuis le mĂȘme module ou depuis plusieurs modules, n'a aucun coĂ»t en terme de performances.
Si on execute modB.py ci-dessous, on aura Ă l'affichage modA exĂ©cutĂ© â
# fichier modA.py
print("modA exécuté")
# fichier modB.py, on execute celui-ci
import modA
import modA # <= pas d'erreur, mais inutile
Dans un module qui est sert de "stockage" de variables, fonctions et classes, on évite de mettre des instructions globales autres que des déclaration de
variables, fonctions et classes hors de toute fonction, car elles seront exécutées lors de la premiÚre importation. Cela peut avoir des effets de bords
indésirables pour le développeur qui importera le module. Donc dans un module qui sert de bibliothÚque à importer on ne met surtout pas
par exemple de code qui affiche avec print(), qui met en pause le programme avec input(), qui manipule des fichiers ou qui
communique sur le réseau hors de fonctions. On met le code dans des fonctions, et le développeur importera le module puis appellera les fonctions
qu'il/elle souhaite, afin de garder la maßtrise du déroulement du programme.
__name__ et le module __main__Chaque module contient un identifiant pré-défini __name__ (avec double _ de chaque cÎté) qui pointe vers un objet de type
str qui a pour valeur le nom du module. Si on execute modB.py ci-dessous â
# fichier modA.py
print(__name__)
# fichier modB.py, on execute celui-ci
import modA
print(modA.__name__)
print(__name__)
modA
modA
__main__
On peut voir que la variable "__name__" dans modA vaut bien "modA".
Par contre, remarquez une chose spĂ©ciale: â la variable __name__ dans modB.py ne vaut pas "modB" mais la string
"__main__". Le module qui est exécuté par le programme Python (par exemple avec la commande python modB.py), devient le module
principal, le point d'entrée du programme, et son module prend le nom spécial "__main__". Dans replit le module qui est exécuté est le
fichier main.py par défaut, quand on clique sur le bouton Run.
Ce qu'on appelle d'habitude contexte global ou environnement global, pour désigner tout ce qui est extérieur à des fonctions et classes, est en fait global au module dans lequel on se situe. Il n'existe pas de contexte vraiment totalement global dans un programme, seulement des modules chacun ayant leur contexte global. Le véritable terme qu'on devrait employer serait namespace du module, mais par abus de langage on dit souvent contexte global du module ou environnement global du module.
dir()Vous pouvez lister le nom de tous les éléments déclarés dans un module avec la fonction dir(), qui prend un objet en paramÚtre, et retourne
une liste de tous les identifiants (sous forme de str) qui sont dans le namespace de l'objet. Exemple:
# fichier modA.py
une_variable = 'a'
def une_fonction():
pass
# fichier modB.py, on execute celui-ci
import modA
print(dir(modA))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__',
'__package__', '__spec__', 'une_fonction', 'une_variable']
â On obtient une liste de str, oĂč chaque Ă©lĂ©ment est le nom d'un identifiant: variable, fonction, classe etc.... Remarquez qu'il y a
beaucoup d'identifiants pré-définies dans le module, ceux entourés de deux caractÚres underscore _, qu'on peut ignorer pour l'instant. Mais
on retrouve la variable __name__, et on peut voir notre fonction une_fonction et notre variable une_variable Ă la
fin.
Sans paramÚtres dir() retourne la liste de tous les identifiants dans le namespace dans lequel elle est appelée, donc par exemple dans le
contexte global pour avoir les identifiants du module dans lequel elle est appelée.
vars() et globals()vars() fait Ă peu prĂšs la mĂȘme chose que dir() mais
retourne un dictionnaire au lieu d'une liste, qui a pour clé les identifiants, et pour valeurs les objets associés. Soit pour un objet/namespace en
particulier passé en paramÚtre, soit pour le namespace depuis lequel elle est appelée si aucun paramÚtre ne lui est passé.
La fonction prédéfinie globals() est trÚs similaire
Ă vars() mais ne prend jamais aucun paramĂštre, et peu importe oĂč elle est appelĂ©e elle retourne toujours tous les identifiants du contexte
global du module dans lequel elle est appelée, et leurs objets associés, dans un dictionnaire comme pour vars().
Prenons le code ci-dessous, et voyons les différents namespaces qui sont créés automatiquement:
(Attention, les objets sont toujours stockés dans l'espace unique des objets (voir partie sur le modÚle de données), mais pour rendre le schéma plus lisible les objets sont montrés éparpillés autour du namespace.)
Le namespace ci-dessous est celui une fois la totalité du fichier exécuté ou importé. C'est important de le préciser, car ligne aprÚs ligne des nouveaux identifiants sont créés et sont ajoutés dans le namespace du module, donc les noms présents ou pas dans un namespace à un point précis du programme dépendent des lignes qui ont été exécutées jusqu'à ce point.
(Attention, les objets sont toujours stockés dans l'espace unique des objets (voir partie sur le modÚle de données), mais pour rendre le schéma plus lisible les objets sont montrés éparpillés autour du namespace.)
(Attention, les objets sont toujours stockés dans l'espace unique des objets (voir partie sur le modÚle de données), mais pour rendre le schéma plus lisible les objets sont montrés éparpillés autour du namespace.)
Les namespaces des objets sont un peu spĂ©ciaux, car on n'exĂ©cute jamais du code dans le "contexte" d'un objet, mais dans des mĂ©thodes, oĂč on y fait rĂ©fĂ©rence toujours par le paramĂštre "self", comme dans le constructeur tel qu'illustrĂ© dans le schĂ©ma plus haut. C'est pour cette raison que les autres variables n'apparaissent pas sur le schĂ©ma ci-dessous, car la notion de visibilitĂ© n'a pas vraiment de sens, seule la notion de nom DANS le namespace de chaque objet est pertinente. Mais conceptuellement on peut reprĂ©senter les namespaces des deux instances "mon_objet_1" et "mon_objet_2" comme ci-dessous.
(Attention, les objets sont toujours stockés dans l'espace unique des objets (voir partie sur le modÚle de données), mais pour rendre le schéma plus lisible les objets sont montrés éparpillés autour du namespace.)
De trĂšs nombreuses fonctionnalitĂ©s de Python sont sĂ©parĂ©es dans des modules qu'il faut importer spĂ©cifiquement. Ces modules ne font pas partie du langage en lui-mĂȘme, mais forment ce qu'on appelle la bibliothĂšque standard. Une implĂ©mentation de Python n'est pas obligĂ©e d'inclure cette bibliothĂšque standard. Vous pouvez voir la liste exhaustive de tous les modules standards dans la documentation officielle.
Il existe un module particulier nommé builtins (qui signifie "intégrés" en anglais) qui est importé automatiquement d'une façon qui lui est
particuliÚre. Il contient les fonctions qu'on a utilisées depuis toujours, comme print(), input() etc... Pour obtenir une
rĂ©fĂ©rence vers builtins (objet de type module) il faut quand mĂȘme l'importer explicitement avec import
builtins. Une implémentation de Python DOIT inclure ce module, car il contient des éléments fondamentaux du langage. Vous avez dans la
documentation officielle la liste complĂšte de toutes les fonctions, tous les types, toutes les constantes
et exceptions qui existent dans le module builtins.