Ayant déjà utilisé des packages plats, je ne m'attendais pas au problème que j'ai rencontré avec les packages imbriqués. Voici…
Disposition du répertoire
dir
|
+-- test.py
|
+-- package
|
+-- __init__.py
|
+-- subpackage
|
+-- __init__.py
|
+-- module.py
Contenu de init .py
Les deux package/__init__.py
et package/subpackage/__init__.py
sont vides.
Contenu de module.py
# file `package/subpackage/module.py`
attribute1 = "value 1"
attribute2 = "value 2"
attribute3 = "value 3"
# and as many more as you want...
Contenu de test.py
(3 versions)
Version 1
# file test.py
from package.subpackage.module import *
print attribute1 # OK
C'est la manière mauvaise et dangereuse d'importer des choses (tout importer en vrac), mais cela fonctionne.
Version 2
# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
from module import attribute1
Un moyen plus sûr d'importer, élément par élément, mais ça échoue, Python ne veut pas ça: échoue avec le message: "Aucun module nommé module". Pourtant …
# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
print module # Surprise here
… Dit <module 'package.subpackage.module' from '...'>
. Donc c'est un module, mais ce n'est pas un module / -P 8-O ... euh
Version 3
# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK
Celui-ci fonctionne. Donc, vous êtes soit obligé d'utiliser le préfixe overkill tout le temps, soit d'utiliser la méthode non sécurisée comme dans la version n ° 1 et interdite par Python d'utiliser la méthode sûre et pratique? La meilleure façon, qui est sûre et évite les longs préfixes inutiles, est la seule que Python rejette? Est-ce parce qu'il aime import *
ou parce qu'il aime les préfixes trop longs (ce qui n'aide pas à appliquer cette pratique)?.
Désolé pour les mots difficiles, mais cela fait deux jours que j'essaye de contourner ce comportement stupide. À moins que je ne me sois totalement trompé quelque part, cela me laissera le sentiment que quelque chose est vraiment cassé dans le modèle de paquetage et de sous-paquet de Python.
Remarques
- Je ne veux pas compter sur
sys.path
, pour éviter les effets secondaires globaux, ni sur les*.pth
fichiers, qui ne sont qu'une autre façon de jouersys.path
avec les mêmes effets globaux. Pour que la solution soit propre, elle doit être uniquement locale. Soit Python est capable de gérer les sous-packages, soit ce n'est pas le cas, mais il ne devrait pas nécessiter de jouer avec la configuration globale pour pouvoir gérer les choses locales. - J'ai aussi essayé d'utiliser les importations dans
package/subpackage/__init__.py
, mais ça n'a rien résolu, ça fait la même chose, et se plaintsubpackage
n'est pas un module connu, alors queprint subpackage
dit que c'est un module (comportement bizarre, encore une fois).
Peut-être que je me trompe complètement (l'option que je préférerais), mais cela me déçoit beaucoup de Python.
Un autre moyen connu à côté des trois que j'ai essayé? Quelque chose que je ne sais pas?
(soupir)
-----% <----- éditer ----->% -----
Conclusion jusqu'à présent (après les commentaires des gens)
Il n'y a rien de tel qu'un vrai sous-package en Python, car toutes les références de package vont uniquement vers un dictionnaire global, ce qui signifie qu'il n'y a pas de dictionnaire local, ce qui implique qu'il n'y a aucun moyen de gérer la référence de package locale.
Vous devez utiliser un préfixe complet ou un préfixe court ou un alias. Un péché:
Version du préfixe complet
from package.subpackage.module import attribute1
# An repeat it again an again
# But after that, you can simply:
use_of (attribute1)
Version de préfixe court (mais préfixe répété)
from package.subpackage import module
# Short but then you have to do:
use_of (module.attribute1)
# and repeat the prefix at every use place
Ou bien, une variante de ce qui précède.
from package.subpackage import module as m
use_of (m.attribute1)
# `m` is a shorter prefix, but you could as well
# define a more meaningful name after the context
Version factorisée
Si cela ne vous dérange pas d'importer plusieurs entités en même temps dans un lot, vous pouvez:
from package.subpackage.module import attribute1, attribute2
# and etc.
Pas dans mon premier goût préféré (je préfère avoir une déclaration d'importation par entité importée), mais c'est peut-être celle que je privilégierai personnellement.
Mise à jour (2012-09-14):
Enfin semble être OK dans la pratique, sauf avec un commentaire sur la mise en page. Au lieu de ce qui précède, j'ai utilisé:
from package.subpackage.module import (
attribute1,
attribute2,
attribute3,
...) # and etc.
__all__
variable contenant une liste des noms à exporter lors de l'importation en étoile. edit: OK, en lisant la réponse de BrenBarn, j'ai compris ce que vous vouliez dire.Réponses:
Vous semblez mal comprendre comment
import
recherche des modules. Lorsque vous utilisez une instruction d'importation, elle recherche toujours le chemin d'accès réel du module (et / ousys.modules
); il n'utilise pas les objets de module dans l'espace de noms local qui existent en raison d'importations précédentes. Quand vous faites:import package.subpackage.module from package.subpackage import module from module import attribute1
La deuxième ligne recherche un package appelé
package.subpackage
et importemodule
depuis ce package. Cette ligne n'a aucun effet sur la troisième ligne. La troisième ligne recherche simplement un module appelémodule
et n'en trouve pas. Il ne "réutilise" pas l'objet appelémodule
que vous avez obtenu à partir de la ligne ci-dessus.En d'autres termes,
from someModule import ...
cela ne signifie pas "du module appelé someModule que j'ai importé plus tôt ..." cela signifie "du module nommé someModule que vous trouvez sur sys.path ...". Il n'y a aucun moyen de construire "incrémentalement" le chemin d'un module en important les packages qui y mènent. Vous devez toujours vous référer au nom complet du module lors de l'importation.Ce que vous essayez d'accomplir n'est pas clair. Si vous voulez seulement importer l'attribut d'objet particulier1, faites-le
from package.subpackage.module import attribute1
et en avez fini avec lui. Vous n'avez jamais à vous soucier de la duréepackage.subpackage.module
une fois que vous avez importé le nom souhaité.Si vous ne voulez avoir accès au module d'accès d' autres noms plus tard, alors vous pouvez le faire
from package.subpackage import module
et, comme vous l' avez vu , vous pouvez le fairemodule.attribute1
et ainsi de suite autant que vous le souhaitez.Si vous voulez les deux --- c'est-à-dire si vous voulez
attribute1
directement accessible et que vous voulezmodule
accessible, faites simplement les deux ci-dessus:from package.subpackage import module from package.subpackage.module import attribute1 attribute1 # works module.someOtherAttribute # also works
Si vous n'aimez pas taper
package.subpackage
même deux fois, vous pouvez simplement créer manuellement une référence locale à l'attribut1:from package.subpackage import module attribute1 = module.attribute1 attribute1 # works module.someOtherAttribute #also works
la source
module.attribute1
est quelque chose que je pensais, mais je pensais qu'il y aurait un moyen d'éviter la nécessité d'un préfixe partout. Je dois donc soit utiliser un préfixe partout, soit créer un alias local en répétant le nom. Pas le style que j'attendais, mais s'il n'y a pas moyen (après tout, je suis habitué à Ada, qui nécessite quelque chose de similaire avec ses déclarations de renommage).La raison n ° 2 échoue est qu'il
sys.modules['module']
n'existe pas (la routine d'importation a sa propre portée et ne peut pas voir lemodule
nom local) et qu'il n'y a pas demodule
module ou de package sur le disque. Notez que vous pouvez séparer plusieurs noms importés par des virgules.from package.subpackage.module import attribute1, attribute2, attribute3
Aussi:
from package.subpackage import module print module.attribute1
la source
sys.modules['name']
que je ne savais pas jusqu'à présent, m'a fait penser que c'était ce que j'avais peur (et BrenBarn confirme): il n'y a rien de tel que de vrais sous-packages en Python.sys.modules
, comme son nom l'indique, est globale, et si toutes les références aux modules reposent sur cela, alors il n'y a rien de tel qu'une référence locale à un module (peut-être venir avec Python 3.x?).import
dans # 2 génère une référence locale à laquellepackage.subpackage.module
liermodule
.Si tout ce que vous essayez de faire est d'obtenir l'attribut1 dans votre espace de noms global, la version 3 semble très bien. Pourquoi est-ce un préfixe excessif?
Dans la version 2, au lieu de
from module import attribute1
tu peux faire
la source
attribute1 = module.attribute1
répète simplement le nom sans valeur ajoutée. Je sais que cela fonctionne, mais je n'aime pas ce style (ce qui ne veut pas dire que je n'aime pas votre réponse).