web-dev-qa-db-fra.com

Python: importer un sous-package ou un sous-module

Ayant déjà utilisé des packages plats, je n'attendais pas le problème rencontré avec les packages imbriqués. Voici…

Mise en page du répertoire

dir
 |
 +-- test.py
 |
 +-- package
      |
      +-- __init__.py
      |
      +-- subpackage
           |
           +-- __init__.py
           |
           +-- module.py

Contenu de init. Py

Tous 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 façon mauvaise et peu sûre 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 il échoue, Python ne veut pas ceci: échoue avec le message: "Aucun module nommé module". Cependant…

# 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 obligé d'utiliser le préfixe overkill tout le temps ou d'utiliser la méthode non sécurisée comme dans la version 1 et interdite par Python pour utiliser la méthode sécurisée et pratique? La meilleure façon, qui est sécurisée et éviter les préfixes longs non nécessaires est le seul qui 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 durs, mais cela fait deux jours que j'essaie de contourner ce comportement stupide. À moins que je ne sois totalement trompé quelque part, cela me laissera le sentiment que quelque chose est vraiment cassé dans le modèle de paquet et de sous-paquet de Python.

Remarques

  • Je ne veux pas compter sur sys.path, pour éviter les effets secondaires globaux, ni sur *.pth fichiers, qui ne sont qu’un autre moyen de jouer avec sys.path avec les mêmes effets globaux. Pour que la solution soit propre, elle doit être locale uniquement. Soit Python est capable de gérer les sous-packages, mais ce n’est pas nécessaire, mais cela ne devrait pas nécessiter de jouer avec la configuration globale pour pouvoir gérer les éléments locaux.
  • J'ai aussi essayé d'utiliser les importations dans package/subpackage/__init__.py, mais il n’a rien résolu, il fait de même et se plaint subpackage n’est pas un module connu, alors que print subpackage dit que c'est un module (comportement étrange, encore).

Peut-être que je me trompe complètement (l'option que je préférerais), mais cela me déçoit beaucoup à propos de Python.

Un autre moyen connu à côté des trois j'ai essayé? Quelque chose que je ne connais pas?

(soupir)

-----% <----- edit ----->% -----

Conclusion jusqu'ici (après les commentaires des gens)

Il n'y a rien de tel qu'un vrai sous-paquet dans Python, car toutes les références de paquet vont à 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 paquet local.

Vous devez utiliser un préfixe complet ou un préfixe court ou un alias. Un péché:

Version complète du préfixe

from package.subpackage.module import attribute1
# An repeat it again an again
# But after that, you can simply:
use_of (attribute1)

Version du 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 vous ne voulez pas 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):

Finalement, ça 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.
67
Hibou57

Vous semblez avoir mal compris comment import cherche des modules. Lorsque vous utilisez une instruction import, elle toujours recherche le chemin du module actuel (et/ou sys.modules); il n'utilise pas le module objets dans l'espace de noms local qui existe en raison d'importations précédentes. Quand tu fais:

import package.subpackage.module
from package.subpackage import module
from module import attribute1

La deuxième ligne cherche un paquet nommé package.subpackage et importe module de ce paquet. 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 ... ne signifie pas "du module appelé someModule que j’avais importé auparavant ...", cela signifie "du module nommé someModule que vous trouvez sur sys.path ...". Il n'y a aucun moyen de "incrémenter" le chemin d'un module en important les packages qui y mènent. Vous devez toujours faire référence 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'objet attribut1 particulier, faites juste from package.subpackage.module import attribute1 et en finir avec ça. Vous ne devez jamais vous soucier de la longue package.subpackage.module une fois que vous avez importé le nom de votre choix.

Si vous faites voulez avoir accès au module pour accéder ultérieurement à d'autres noms, vous pouvez alors faire from package.subpackage import module et, comme vous l'avez vu, vous pouvez alors faire module.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 vous voulez module accessible, faites simplement les deux choses 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 à attribut1:

from package.subpackage import module
attribute1 = module.attribute1
attribute1 # works
module.someOtherAttribute #also works
55
BrenBarn

La raison n ° 2 échoue parce que sys.modules['module'] _ n'existe pas (la routine d'importation a sa propre portée et ne peut pas voir le nom local module.) et il n'y a pas de module ni de package module sur disque. Notez que vous pouvez séparer plusieurs noms importés par des virgules.

from package.subpackage.module import attribute1, attribute2, attribute3

Également:

from package.subpackage import module
print module.attribute1
8

Si tout ce que vous essayez de faire est d’obtenir attribut1 dans votre espace de noms global, la version 3 semble très bien. Pourquoi est-ce préfixe overkill?

Dans la version 2, au lieu de

from module import attribute1

tu peux faire

attribute1 = module.attribute1
0