ChangeLog
--2020-04-18: Correction du comportement dans le module __main__
.
Jetons un coup d'œil aux packages et modules Python, que de nombreuses personnes semblent comprendre et utiliser. Ce n'est pas très organisé, je vais donc le réécrire un jour.
J'ai du code d'apprentissage automatique Python écrit par quelqu'un d'autre.
Il y a un fichier que vous voulez exécuter à l'emplacement . / A / b / c.py
du répertoire courant.
Ce fichier est écrit dans un format courant qui fonctionne de deux manières selon la valeur de «name».
À l'intérieur, . / A / b / d.py
est ** absolument importé ** par ʻimport d`.
À ce stade, en supposant que PYTHONPATH
est laissé par défaut, lequel fonctionne correctement quand il est réglé sur python. / A / b / c.py
ou quand il est réglé sur ʻimport abcà partir d'un autre fichier python. Changera. Si ce dernier ne fonctionne pas lorsque le premier fonctionne, et que le second fonctionne, si vous réécrivez
c.py` en importation relative, le premier ne fonctionnera pas.
Ce qui se passe, c'est la motivation pour étudier cette fois.
Permettez-moi d'expliquer un peu plus cette dernière situation. Vous exécutez pytest ./test/<test file> .py
ou python -m pytest ./test/ <test file> .py
pour les tests unitaires. Le fait est que le fichier de test n'est pas dans le répertoire courant.
J'ai parcouru la version v3.8.2 des deux.
Notez que le didacticiel lit les fichiers de niveau supérieur, c'est-à-dire les fichiers lancés par python <file>
en tant que ** scripts **.
Le plus simple
importe ** module **
m`m
est un ** fichier Python ** appelé m.py
.m.py
avec m.n
.Je comprends.
__name__
. Dans le m
importé par la méthode ci-dessus, __name__
devient " m "
python
, __name__
devient " __ main__ "
. Pensez à l'environnement de niveau supérieur comme étant un module __main__
.sys.path
)Dans l'exemple ci-dessus, le module m
à importer, c'est-à-dire le fichier m.py
, se trouve au début de la liste des répertoires définie sur sys.path
(la valeur de la variable path
du module sys
). Il est recherché dans l'ordre de. Par défaut, ils sont classés dans l'ordre suivant:
python
)PYTHONPATH
Ici, le premier est l'auteur-compositeur **, le répertoire contenant le fichier exécuté est recherché à la place du répertoire courant. ** ** Les deux sont identiques uniquement si vous exécutez le fichier dans le répertoire courant.
Maintenant, la raison du problème que j'ai écrit dans "Motivation" est presque claire. Si vous exécutez python. / A / b / c.py
avec l'ancienne méthode, la première règle place. / A / b
au début de sys.path
. Ensuite, ʻimport dest d'abord recherché pour ce répertoire, et l'importation réussit car il a en fait
. / A / b / d.py`.
D'autre part, lorsque pytest est exécuté, sys.path
commence par. / Test
, donc ʻimport abc ne passe pas en premier lieu, et même si cela est évité en définissant
PYTHONPATH,
c ʻImport d dans .py
ne passe pas cette fois.
Si ʻimport dest changé en
from .import d` (est-ce que c'était?) Et ** importation relative **, l'ancien modèle ne passera certainement pas [confirmation requise].
sys.path
Si la structure de répertoires du module est compliquée, ou si vous essayez de réaliser une importation dynamique,
sys.path
dans votre programme PythonOn dit que ** est un mauvais coup ** [Source]. Il semble correct d'utiliser pleinement ʻimportlib`.
Un ** package ** est une collection des modules ci-dessus dans un répertoire. Le nom du répertoire devient le nom du package. Les répertoires peuvent avoir une structure hiérarchique, qui correspond au chemin du package (séquence de noms de packages séparés par des points).
--ʻImport a.b.cimporte le module
c du package ʻa.b
. Autrement dit, importez «a / b / c.py» dans le répertoire cible de recherche du module.
p / __ init __. Py
** doit être inclus pour que le répertoire p
soit reconnu comme un package p
.De là, beaucoup de gens semblent ne le comprendre que d'une manière ou d'une autre. Je le suis aussi.
--Lorsque le package lui-même, par exemple
p, est importé, le résultat de l'exécution de
p / __ init __. Py en tant que module va dans l'espace de noms
p`., ʻa
est d'abord importé, puis ʻa.b est importé. Autrement dit, ʻa / __ init __. Py
est exécuté, puis ʻa / b / __ init __. Py` est exécuté., ʻa / b / c.py
est exécuté après l'exécution du fichier comme ci-dessus.Lorsque le package «p» est importé, la variable «path» dans «p / __ init __. Py» peut faire référence à la chaîne représentant le répertoire de «p».
from ... import ...
avec une simple instruction d'importation, vous devez utiliser ʻa.b.c.n
pour référencer le nom n
du module c
.
--Cependant, vous pouvez y faire référence avec c.n
en définissant à partir de a.b import c
.
--à partir de l'importation
peut être
--Nom n
. Si vous faites from a.b.c import n
, vous ne pouvez y faire référence qu'avec n
après cela.
--Module «c». Comme je l'ai expliqué plus tôt
--Package «b». Avec à partir d'une importation b
. Après cela, vous ne pouvez faire référence au package «b» que par «b» au lieu de «a.b».Si vous importez le paquet b
comme ci-dessus, b / __ init __. Py
sera exécuté. Ce fichier est vide par défaut, et quand il est vide, l'interpréteur ne fait rien, donc importer ** b
ne rend pas ʻa.b.c` accessible. ** **
Lors de l'importation d'autres modules, l'importation absolue mentionnée ci-dessus est généralement utilisée. La recherche des modules se fait pour sys.path
.
Vous pouvez ** importer relative ** en utilisant l'instruction from import
ci-dessus.
Dans le module importé (c'est-à-dire le fichier .py
)
from . import m
from .. import m
from ..p import q
Vous pouvez importer des packages, des modules, des noms, etc. les uns par rapport aux autres. À ce stade, .
et ..
sont résolus en fonction du ** chemin du package ** où le module actuel existe.
Autrement dit, si le module actuel est ʻa.b.m, c'est-à-dire que le package auquel il appartient est ʻa.b
, alors .
est ʻa.b et
.. est ʻa
.
Notez que le module __main__
n'a pas de chemin de package. Une conséquence importante de ceci est que vous ne pouvez pas effectuer d'importations relatives à partir de modules exécutés en tant que ** __main__
. ** **
De plus, je pense que c'est correct de faire quelque chose comme ʻimport ..p.q.m`, mais il semble que cela ne peut pas être fait
Cela résout la deuxième question. La réécriture de ʻa / b / c.pyen importation relative comme
from .import d fonctionne très bien si
c est importé avec ʻimport abc
, mais python a / Lorsqu'il est exécuté en tant que b / c.py
, c.py
se comporte comme module __main__
et l'importation relative n'est pas disponible. En conséquence, j'ai été déçu du comportement.
De plus, en conséquence, les modules importés à partir du code que vous souhaitez utiliser à la fois comme scripts et comme modules, tels que «a / b / c.py» dans l'exemple, doivent être «pip install». De cette façon, vous pouvez toujours utiliser l'importation absolue, vous pouvez donc utiliser l'une ou l'autre méthode.
Il a également été confirmé que le comportement d'importation diffère selon la façon dont pytest est démarré.
J'ai essayé de mettre une instruction d'impression dans le script de test (-s
est une option pour empêcher pytest de capturer la sortie standard)
--pytest -s test / <fichier de test.py>
démarre sys.path
avec le répertoire test
, suivi des valeurs par défaut du système
--Mais si vous faites python -m pytest -s test / <test file.py>
, la chaîne vide " "
est ajoutée après le répertoire test
. Cela signifie peut-être le répertoire actuel.
Certaines des raisons à cela sont également connues. Si vous spécifiez un module avec -m
lors du démarrage de l'interpréteur Python, le répertoire courant est ajouté au début de sys.path
. Dans ce cas, c'est peut-être pytest, pas l'interpréteur, qui ajoute «test» à «sys.path». Parce que dans ce cas, -s test / <test file.py>
est juste un argument, pas le nom du script à exécuter [examine].
De plus, lorsque vous installez la commande avec pip, vérifiez les paramètres tels que sys.path
lorsque vous lancez la commande.
Toute erreur est la bienvenue.
Recommended Posts