J'ai répondu à une question concernant les importations absolues en Python, que je pensais comprendre en lisant le journal des modifications Python 2.5 et en accompagnant le PEP . Cependant, en installant Python 2.5 et en essayant de créer un exemple d'utilisation correcte from __future__ import absolute_import
, je me rends compte que les choses ne sont pas si claires.
Directement à partir du journal des modifications lié ci-dessus, cette déclaration a résumé avec précision ma compréhension du changement d'importation absolu:
Disons que vous avez un répertoire de packages comme celui-ci:
pkg/ pkg/__init__.py pkg/main.py pkg/string.py
Cela définit un package nommé
pkg
contenant les souspkg.main
-pkg.string
modules et .Considérez le code dans le module main.py. Que se passe-t-il s'il exécute l'instruction
import string
? Dans Python 2.4 et versions antérieures, il cherchera d'abord dans le répertoire du package pour effectuer une importation relative, trouve pkg / string.py, importe le contenu de ce fichier en tant quepkg.string
module, et ce module est lié au nom"string"
dans l'pkg.main
espace de noms du module.
J'ai donc créé cette structure de répertoires exacte:
$ ls -R
.:
pkg/
./pkg:
__init__.py main.py string.py
__init__.py
et string.py
sont vides. main.py
contient le code suivant:
import string
print string.ascii_uppercase
Comme prévu, son exécution avec Python 2.5 échoue avec un AttributeError
:
$ python2.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Cependant, plus loin dans le changelog 2.5, nous trouvons ceci (emphase ajoutée):
Dans Python 2.5, vous pouvez changer
import
le comportement de l 'importation absolue en utilisant unefrom __future__ import absolute_import
directive. Ce comportement d'importation absolue deviendra le comportement par défaut dans une version future (probablement Python 2.7). Une fois que les importations absolues sont la valeur par défaut,import string
trouvera toujours la version de la bibliothèque standard.
J'ai donc créé pkg/main2.py
, identique main.py
mais avec la future directive d'importation supplémentaire. Cela ressemble maintenant à ceci:
from __future__ import absolute_import
import string
print string.ascii_uppercase
L'exécution de ceci avec Python 2.5, cependant ... échoue avec un AttributeError
:
$ python2.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
Cela contredit carrément la déclaration qui import string
trouvera toujours la version std-lib avec les importations absolues activées. De plus, malgré l'avertissement selon lequel les importations absolues sont programmées pour devenir le comportement "nouveau par défaut", j'ai rencontré le même problème en utilisant à la fois Python 2.7, avec ou sans la __future__
directive:
$ python2.7 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
$ python2.7 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print string.ascii_uppercase
AttributeError: 'module' object has no attribute 'ascii_uppercase'
ainsi que Python 3.5, avec ou sans (en supposant que l' print
instruction soit modifiée dans les deux fichiers):
$ python3.5 pkg/main.py
Traceback (most recent call last):
File "pkg/main.py", line 2, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
$ python3.5 pkg/main2.py
Traceback (most recent call last):
File "pkg/main2.py", line 3, in <module>
print(string.ascii_uppercase)
AttributeError: module 'string' has no attribute 'ascii_uppercase'
J'ai testé d'autres variantes de cela. Au lieu de string.py
, j'ai créé un module vide - un répertoire nommé string
contenant uniquement un vide __init__.py
- et au lieu d'émettre les importations en provenance main.py
, je cd
« d pour pkg
et exécuter les importations directement à partir du REPL. Aucune de ces variations (ni une combinaison d’entre elles) n’a changé les résultats ci-dessus. Je ne peux pas concilier cela avec ce que j'ai lu sur la __future__
directive et les importations absolues.
Il me semble que cela est facilement explicable par ce qui suit (cela provient de la documentation Python 2 mais cette déclaration reste inchangée dans la même documentation pour Python 3):
sys.path
(...)
Comme initialisé au démarrage du programme, le premier élément de cette liste,
path[0]
est le répertoire contenant le script qui a été utilisé pour appeler l'interpréteur Python. Si le répertoire de script n'est pas disponible (par exemple si l'interpréteur est appelé de manière interactive ou si le script est lu à partir de l'entrée standard),path[0]
est la chaîne vide, qui dirige Python vers les modules de recherche dans le répertoire courant en premier.
Alors qu'est-ce que je manque? Pourquoi la __future__
déclaration ne fait-elle apparemment pas ce qu'elle dit, et quelle est la résolution de cette contradiction entre ces deux sections de la documentation, ainsi qu'entre le comportement décrit et le comportement réel?
la source
Réponses:
Le journal des modifications est mal rédigé.
from __future__ import absolute_import
ne se soucie pas de savoir si quelque chose fait partie de la bibliothèque standard, etimport string
ne vous donnera pas toujours le module de bibliothèque standard avec les importations absolues activées.from __future__ import absolute_import
signifie que si vousimport string
, Python recherchera toujours unstring
module de niveau supérieur , plutôt quecurrent_package.string
. Cependant, cela n'affecte pas la logique utilisée par Python pour décider quel fichier est lestring
module. Quand tu faispkg/script.py
ne ressemble pas à une partie d'un package en Python. En suivant les procédures normales, lepkg
répertoire est ajouté au chemin et tous les.py
fichiers dupkg
répertoire ressemblent à des modules de niveau supérieur.import string
trouvepkg/string.py
non pas parce qu'il effectue une importation relative, mais parce qu'ilpkg/string.py
semble être le module de niveau supérieurstring
. Le fait que ce ne soit pas lestring
module de bibliothèque standard ne s'affiche pas.Pour exécuter le fichier dans le cadre du
pkg
package, vous pouvez faireDans ce cas, le
pkg
répertoire ne sera pas ajouté au chemin. Cependant, le répertoire courant sera ajouté au chemin.Vous pouvez également ajouter un passe-partout pour
pkg/script.py
que Python le traite comme faisant partie dupkg
package, même lorsqu'il est exécuté en tant que fichier:Cependant, cela n'affectera pas
sys.path
. Vous aurez besoin d'une manipulation supplémentaire pour supprimer lepkg
répertoire du chemin, et sipkg
le répertoire parent de n'est pas sur le chemin, vous devrez également le coller sur le chemin.la source
import string
si vous l'observez accidentellement, au moins sans fouillersys.modules
. N'est-ce pas ce que l'from __future__ import absolute_import
on entend empêcher? Qu'est ce que ça fait? (PS, je ne suis pas le downvoter.)sys.path
fonctionne, et la question réelle n'a pas du tout été abordée. Autrement dit, que faitfrom __future__ import absolute_import
réellement?sys.modules
ne vous donnera pas lestring
module de bibliothèque standard si vous l'avez observé avec votre propre module de niveau supérieur.from __future__ import absolute_import
ne vise pas à empêcher les modules de niveau supérieur d'observer les modules de niveau supérieur; il est censé empêcher les modules internes au paquet d'observer les modules de niveau supérieur. Si vous exécutez le fichier dans le cadre dupkg
package, les fichiers internes du package cessent de s'afficher en tant que niveau supérieur.pkg
existe un package sur le chemin de recherche d'importation, cela devrait êtrepython -m pkg.main
.-m
a besoin d'un nom de module, pas d'un chemin de fichier.La différence entre les importations absolues et relatives n'entre en jeu que lorsque vous importez un module à partir d'un package et que ce module importe un autre sous-module à partir de ce package. Regarde la différence:
En particulier:
Notez que le
python2 pkg/main2.py
comportement est différent du lancementpython2
puis de l'importationpkg.main2
(ce qui équivaut à utiliser le-m
commutateur).Si vous souhaitez exécuter un sous-module d'un package, utilisez toujours le
-m
commutateur qui empêche l'interpréteur de chaîner lasys.path
liste et gère correctement la sémantique du sous-module.De plus, je préfère de loin utiliser des importations relatives explicites pour les sous-modules de package car ils fournissent plus de sémantique et de meilleurs messages d'erreur en cas d'échec.
la source
python2 pkg/main2.py
a- t- il un comportement différent du lancement de python2 puis de l'importation de pkg.main2?pkg/main2.py
python (version 2) ne le traite paspkg
comme un package. Lors de son utilisationpython2 -m pkg.main2
ou de son importation, tenez compte du fait qu'ilpkg
s'agit d'un package.