131 votes

Python : importation d'un sous-paquet ou d'un sous-module

Ayant déjà utilisé des paquets plats, je ne m'attendais pas au problème que j'ai rencontré avec les paquets imbriqués. Voici

Mise en page du répertoire

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

Contenu de init .py

Les deux sites package/__init__.py y 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 la plus mauvaise et la moins sûre d'importer des choses (importer tout en bloc), mais cela fonctionne.

Version 2

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
from module import attribute1

Une façon plus sûre d'importer, élément par élément, mais cela échoue, Python ne veut pas de cela : échoue avec le message : "No module named 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 ... uh

Version 3

# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK

Celui-ci fonctionne. Donc, soit vous êtes forcé d'utiliser le préfixe overkill tout le temps, soit vous utilisez la maniÃ?re non sà "re comme dans la version 1 et Python vous interdit d'utiliser la maniÃ?re sà "re et pratique ? La meilleure méthode, qui est sûre et évite les préfixes longs inutiles, est la seule que Python rejette ? Est-ce parce qu'il aime import * ou parce qu'il adore les préfixes trop longs (ce qui ne contribue pas à faire respecter cette pratique) ?

Désolé pour les mots durs, mais c'est deux jours que j'essaie de contourner ce comportement stupide. À moins que je ne me sois totalement trompé quelque part, cela me laisse le sentiment que quelque chose est vraiment cassé dans le modèle de paquet et de sous-paquet de Python.

Notas

  • Je ne veux pas compter sur sys.path pour éviter les effets secondaires globaux, ni sur *.pth qui sont juste un autre moyen de jouer avec les fichiers sys.path avec les mêmes effets globaux. Pour que la solution soit propre, il faut qu'elle soit uniquement locale. Soit Python est capable de gérer les sous-paquets, soit il ne l'est pas, mais il ne devrait pas avoir besoin de jouer avec la configuration globale pour être capable de gérer les choses locales.
  • J'ai également essayé d'utiliser les importations dans package/subpackage/__init__.py mais cela n'a rien résolu, il fait la même chose et se plaint. subpackage n'est pas un module connu, tandis que print 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 à propos de Python.

Y a-t-il un autre moyen connu en dehors des trois que 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-paquetage en Python, car toutes les références de paquetage vont uniquement dans 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 les références locales de paquetage.

Vous devez utiliser soit le préfixe complet, soit le préfixe court, soit l'alias. Comme dans :

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 à 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 encore, une variation 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 l'importation de plusieurs entités en une seule fois dans un lot ne vous dérange pas, vous pouvez le faire :

from package.subpackage.module import attribute1, attribute2
# and etc.

Ce n'est pas mon premier goût (je préfère avoir une déclaration d'importation par entité importée), mais c'est peut-être celui que je privilégierai personnellement.

Mise à jour (2012-09-14) :

Finalement, tout semble correct dans la pratique, à l'exception d'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.

108voto

BrenBarn Points 63718

Vous semblez mal comprendre comment import recherche des modules. Lorsque vous utilisez une instruction d'importation, elle toujours recherche le chemin réel du module (et/ou sys.modules ) ; il n'utilise pas le module objets dans l'espace de nom local qui existent à cause des importations précédentes. Quand vous le faites :

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

La deuxième ligne recherche un paquet appelé package.subpackage et les importations 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'ai importé précédemment..." mais "du module nommé someModule que vous trouvez dans sys.path...". Il n'y a aucun moyen de construire "incrémentalement" le chemin d'un module en important les paquets 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'obtenir n'est pas clair. Si vous ne voulez importer que l'objet particulier attribut1, il suffit de faire from package.subpackage.module import attribute1 et en finir avec ça. Vous n'avez pas à vous soucier de la longue package.subpackage.module une fois que vous avez importé le nom que vous voulez.

Si vous faire vous voulez avoir accès au module pour accéder à d'autres noms plus tard, alors vous pouvez le 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 juste 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 à l'attribut1 :

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

11voto

La raison pour laquelle le numéro 2 échoue est que sys.modules['module'] n'existe pas (la routine d'importation a son propre champ d'application et ne peut pas voir l'adresse de l'utilisateur). module nom local), et il n'y a pas de module module ou paquet 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

0voto

Si tout ce que vous essayez de faire est d'obtenir l'attribut1 dans votre espace de noms global, la version 3 semble parfaite. Pourquoi le préfixe est-il excessif ?

Dans la version 2, au lieu de

from module import attribute1

vous pouvez faire

attribute1 = module.attribute1

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X