En Python, un package d'espace de noms vous permet de diffuser du code Python entre plusieurs projets. Ceci est utile lorsque vous souhaitez publier des bibliothèques associées sous forme de téléchargements séparés. Par exemple, avec les répertoires Package-1
et Package-2
in PYTHONPATH
,
Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py
l'utilisateur final peut import namespace.module1
et import namespace.module2
.
Quelle est la meilleure façon de définir un package d'espace de noms afin que plusieurs produits Python puissent définir des modules dans cet espace de noms?
python
namespaces
package
Joeforker
la source
la source
Réponses:
TL; DR:
Sur Python 3.3, vous n'avez rien à faire, n'en mettez tout simplement pas
__init__.py
dans les répertoires de vos packages d'espace de noms et cela fonctionnera. Sur la version antérieure à la version 3.3, choisissez lapkgutil.extend_path()
solution plutôt quepkg_resources.declare_namespace()
celle, car elle est à l'épreuve du temps et déjà compatible avec les packages d'espace de noms implicites.Python 3.3 introduit les packages d'espace de noms implicites, voir PEP 420 .
Cela signifie qu'il existe désormais trois types d'objets qui peuvent être créés par un
import foo
:foo.py
fichierfoo
contenant un__init__.py
fichierfoo
sans aucun__init__.py
fichierLes packages sont aussi des modules, mais ici je veux dire "module non-package" quand je dis "module".
Il recherche
sys.path
d'abord un module ou un package standard. S'il réussit, il arrête la recherche et crée et initialise le module ou le package. S'il n'a trouvé aucun module ou package normal, mais qu'il a trouvé au moins un répertoire, il crée et initialise un package d'espace de noms.Les modules et les packages normaux sont
__file__
définis sur le.py
fichier à partir duquel ils ont été créés. Les packages standard et d'espace de noms sont__path__
définis sur le ou les répertoires à partir desquels ils ont été créés.Lorsque vous le faites
import foo.bar
, la recherche ci-dessus se produit d'abord pourfoo
, puis si un package a été trouvé, la recherchebar
est effectuée avecfoo.__path__
comme chemin de recherche au lieu desys.path
. Sifoo.bar
on trouve,foo
etfoo.bar
sont créés et initialisés.Alors, comment se mélangent les packages standards et les packages d'espace de noms? Normalement, ce n'est pas le cas, mais l'ancienne
pkgutil
méthode de package d'espace de noms explicite a été étendue pour inclure des packages d'espace de noms implicites.Si vous avez un package régulier existant qui a un
__init__.py
comme ceci:... le comportement hérité consiste à ajouter tous les autres packages réguliers sur le chemin recherché à son
__path__
. Mais dans Python 3.3, il ajoute également des packages d'espace de noms.Vous pouvez donc avoir la structure de répertoires suivante:
... et tant que les deux
__init__.py
ont lesextend_path
lignes (etpath1
,path2
etpath3
sont dans votresys.path
)import package.foo
,import package.bar
etimport package.baz
tout fonctionnera.pkg_resources.declare_namespace(__name__)
n'a pas été mis à jour pour inclure les packages d'espace de noms implicites.la source
namespace_packages
option? Et la__import__('pkg_resources').declare_namespace(__name__)
chose?namespace_packages=['package']
lesetup.py
?namespace_packages=['package']
, setup.py ajoutera unnamespace_packages.txt
dans EGG-INFO. Je ne connais toujours pas les impacts…pkg_resources.declare_namespace
overpkgutil.extend_path
est qu'il continuera à surveillersys.path
. De cette façon, si un nouvel élément est ajoutésys.path
après le premier chargement d'un package dans l'espace de noms, les packages de l'espace de noms dans ce nouvel élément de chemin peuvent toujours être chargés. (Un avantage de l'utilisation de__import__('pkg_resources')
overimport pkg_resources
est que vous ne finissez pas parpkg_resources
être exposé commemy_namespace_pkg.pkg_resources
.)sys.path
. Lorsqu'ilsys.path
change, il vérifie si cela affecte le__path__
de n'importe quel espace de noms, et si c'est le cas, il met à jour ces__path__
propriétés.Il existe un module standard, appelé pkgutil , avec lequel vous pouvez «ajouter» des modules à un espace de noms donné.
Avec la structure de répertoires que vous avez fournie:
Vous devriez mettre ces deux lignes dans les deux
Package-1/namespace/__init__.py
etPackage-2/namespace/__init__.py
(*):(* puisque - à moins que vous n'indiquiez une dépendance entre eux - vous ne savez pas lequel d'entre eux sera reconnu en premier - voir PEP 420 pour plus d'informations)
Comme la documentation dit :
À partir de maintenant, vous devriez pouvoir distribuer ces deux packages indépendamment.
la source
__import__
est considéré comme un mauvais style dans ce cas, car il peut être facilement remplacé par une instruction d'importation simple. Plus précisément, pkg_resources est une bibliothèque non standard. Il est livré avec setuptools, donc ce n'est pas un problème. Une recherche rapide sur Google révèle que pkgutil a été introduit dans la version 2.5 et que pkg_resources est antérieur. Néanmoins, pkgutil est une solution officiellement reconnue. L'inclusion de pkg_resources a été, en fait, rejetée dans PEP 365.Package-1/namespace/__init__.py
et àPackage-2/namespace/__init__.py
condition que nous ne sachions pas quel répertoire de package est répertorié en premier?Cette section devrait être assez explicite.
En bref, insérez le code de l'espace de noms
__init__.py
, mettezsetup.py
à jour pour déclarer un espace de noms et vous êtes libre de partir.la source
C'est une vieille question, mais quelqu'un a récemment commenté sur mon blog que ma publication sur les packages d'espaces de noms était toujours pertinente, alors j'ai pensé que je ferais un lien vers elle ici car elle fournit un exemple pratique de la façon de le faire:
https://web.archive.org/web/20150425043954/http://cdent.tumblr.com/post/216241761/python-namespace-packages-for-tiddlyweb
Cela renvoie à cet article pour les principaux tripes de ce qui se passe:
http://www.siafoo.net/article/77#multiple-distributions-one-virtual-package
L'
__import__("pkg_resources").declare_namespace(__name__)
astuce conduit à peu près à la gestion des plugins dans TiddlyWeb et semble jusqu'à présent fonctionner.la source
Vous avez vos concepts d'espace de noms Python à l'envers, il n'est pas possible en python de mettre des packages dans des modules. Les packages contiennent des modules et non l'inverse.
Un package Python est simplement un dossier contenant un
__init__.py
fichier. Un module est tout autre fichier dans un package (ou directement sur lePYTHONPATH
) qui a un.py
extension. Donc, dans votre exemple, vous avez deux packages mais aucun module n'est défini. Si vous considérez qu'un package est un dossier de système de fichiers et qu'un module est un fichier, vous voyez pourquoi les packages contiennent des modules et non l'inverse.Donc, dans votre exemple, en supposant que Package-1 et Package-2 sont des dossiers sur le système de fichiers que vous avez mis sur le chemin Python, vous pouvez avoir les éléments suivants:
Vous avez maintenant un package
namespace
avec deux modulesmodule1
etmodule2
. et à moins que vous n'ayez une bonne raison, vous devriez probablement mettre les modules dans le dossier et n'avoir que cela sur le chemin python comme ci-dessous:la source
zope.x
où un tas de packages connexes sont publiés en tant que téléchargements séparés.