77 votes

Importation de paquets en Python

Je rate probablement quelque chose d'évident, mais bon :

Lorsque vous importez un paquet comme os en python, vous pouvez utiliser tous les submodules/sous-paquets dès le départ. Par exemple, ceci fonctionne :

>>> import os
>>> os.path.abspath(...)

Cependant, j'ai mon propre paquet qui est structuré comme suit :

FooPackage/
  __init__.py
  foo.py

et ici la même logique ne fonctionne pas :

>>> import FooPackage
>>> FooPackage.foo
AttributeError: 'module' object has no attribute 'foo'

Qu'est-ce que je fais de mal ?

71voto

Ben Points 22160

Lorsque vous importez FooPackage Python recherche les répertoires de PYTHONPATH jusqu'à ce qu'il trouve un fichier appelé FooPackage.py ou un répertoire appelé FooPackage contenant un fichier appelé __init__.py . Cependant, ayant trouvé le répertoire du paquet, il fait no analyse alors ce répertoire et importe automatiquement tous les fichiers .py.

Il y a deux raisons à ce comportement. La première est que l'importation d'un module exécute du code Python qui peut prendre du temps, de la mémoire, ou avoir des effets secondaires. Ainsi, vous pourriez vouloir importer a.b.c.d sans nécessairement importer la totalité d'un énorme paquet a . C'est au concepteur du paquet de décider si a est __init__.py importe explicitement ses modules et sous-paquets afin qu'ils soient toujours disponibles, ou laisse au programme client la possibilité de choisir ce qui sera chargé.

La seconde est un peu plus subtile, mais elle est aussi un obstacle. Sans une déclaration d'importation explicite (soit dans FooPackage/__init__.py ou dans le programme client), Python ne sait pas nécessairement quel nom il doit importer foo.py comme. Sur un système de fichiers insensible à la casse (tel qu'utilisé dans Windows), cela pourrait représenter un module nommé foo , Foo , FOO , fOo , foO , FoO , FOo o fOO . Tous ces éléments sont des identifiants Python valides et distincts. Python ne dispose donc pas de suffisamment d'informations à partir du fichier seul pour savoir ce que vous voulez dire. Par conséquent, afin de se comporter de manière cohérente sur tous les systèmes, il exige une déclaration d'importation explicite quelque part pour clarifier le nom, même sur les systèmes de fichiers où des informations complètes sur le cas sont disponibles.

43voto

Rob Wouters Points 6654

Vous devez importer le sous-module :

import FooPackage.foo

Ce que vous faites, c'est chercher foo en FooPackage/__init__.py . Vous pourriez résoudre ce problème en mettant import FooPackage.foo as foo (ou from . import foo ) en FooPackage/__init__.py alors Python sera en mesure de trouver foo là. Mais je vous recommande d'utiliser ma première suggestion.

19voto

Richard Shepherd Points 464

Vous devez ajouter from . import foo au __init__.py dans votre paquet.

15voto

ASDF Points 123

Il existe d'importantes idées fausses qu'il convient de corriger, notamment en ce qui concerne la terminologie. Tout d'abord, en général, lorsque vous pensez que vous importez une package en python, ce que vous importez en réalité est un fichier module . Vous devez utiliser le terme package quand vous pensez en termes de sous-structure de système de fichiers qui vous aide à organiser votre code. Mais du point de vue du code, chaque fois que vous importez un fichier package Python le traite comme un module. Tous les packages sont des modules. Tous les modules ne sont pas des packages. Un module avec l'extension __path__ est considéré comme un paquet.

Vous pouvez vérifier que os est un module. Pour le confirmer, vous pouvez le faire :

import os
print(type(os)) # will print: <type 'module'>

Dans votre exemple, lorsque vous faites import FooPackage , FooPackage est traité et considéré comme un module également, et ses attributs (fonctions, classes, etc.) sont censés être définis dans le module __init__.py . Puisque votre __init__.py est vide, il ne peut pas trouver foo .

En dehors de import les déclarations que vous ne pouvez pas utiliser '.' pour adresser les modules à l'intérieur des modules. La seule exception se produit si un module est importé dans le paquet du parent prévu __init__.py fichier. Pour que tout soit clair, prenons quelques exemples :

Considérez votre structure d'origine :

FooPackage/
  __init__.py
  foo.py

Cas 1 : __init__.py est un fichier vide

#FooPackage imported as a module
import FooPackage 

#foo is not a name defined in `__init__.py`. Error
FooPackage.foo 

#FooPackage.foo imported as a module
import FooPackage.foo

#Error, foo has not been imported. To be able to use foo like this,
#you need to do: import FooPackage.foo as foo
foo.anything

#Not error, if anything is defined inside foo.py
FooPackage.foo.anything

Cas 2 : __init__.py a la ligne import foo en elle :

import FooPackage

#Now this is good. `foo` is sort of considered to be an attribute of 
#FooPackage
FooPackage.foo

Maintenant, supposons que foo n'est plus un module mais un function que vous définissez dans __init__.py . si vous le faites import FooPackage.foo le message d'erreur suivant s'affichera foo n'est pas un module.

0voto

gilberto47831 Points 1

La raison pour laquelle vous pouvez utiliser os.path.func() est parce que os.path est un alias pour le module path de votre système particulier afin de rendre le code plus portable. Les modules os importent le module path correct pour votre système. sys.modules['os.path'] = <path module> . Ce qui se traduirait essentiellement par ntpath.func() pour Windows ou posixpath.func() pour linux.

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