Je n'avais jamais remarqué l'attribut __path__
Qui est défini sur certains de mes packages avant aujourd'hui. Selon la documentation:
Les packages prennent en charge un autre attribut spécial,
__path__
. Ceci est initialisé pour être une liste contenant le nom du répertoire contenant le__init__.py
Du package avant l'exécution du code dans ce fichier. Cette variable peut être modifiée; cela affecte les futures recherches de modules et de sous-packages contenus dans le package.Bien que cette fonctionnalité ne soit pas souvent nécessaire, elle peut être utilisée pour étendre l'ensemble de modules présents dans un package.
Quelqu'un pourrait-il m'expliquer ce que cela signifie exactement et pourquoi je voudrais un jour l'utiliser?
Ceci est généralement utilisé avec pkgutil pour laisser un paquet être disposé sur le disque. Par exemple, zope.interface et zope.schema sont des distributions distinctes (zope
est un "package d'espace de noms"). Vous pouvez avoir zope.interface installé dans /usr/lib/python2.6/site-packages/zope/interface/
, Tandis que vous utilisez zope.schema plus localement dans /home/me/src/myproject/lib/python2.6/site-packages/zope/schema
.
Si vous mettez pkgutil.extend_path(__path__, __name__)
dans /usr/lib/python2.6/site-packages/zope/__init__.py
, Alors zope.interface et zope.schema seront importables car pkgutil aura changé __path__
En ['/usr/lib/python2.6/site-packages/zope', '/home/me/src/myproject/lib/python2.6/site-packages/zope']
.
pkg_resources.declare_namespace
(Partie de Setuptools) est comme pkgutil.extend_path
Mais est plus conscient des zips sur le chemin.
Changer manuellement __path__
Est rare et probablement pas nécessaire, bien qu'il soit utile de regarder la variable lors du débogage des problèmes d'importation avec les packages d'espace de noms.
Vous pouvez également utiliser __path__
Pour le monkeypatching, par exemple, j'ai parfois des distutils monkeypatched en créant un fichier distutils/__init__.py
Qui est au début sys.path
:
import os
stdlib_dir = os.path.dirname(os.__file__)
real_distutils_path = os.path.join(stdlib_dir, 'distutils')
__path__.append(real_distutils_path)
execfile(os.path.join(real_distutils_path, '__init__.py'))
# and then apply some monkeypatching here...
Si vous changez __path__
, vous pouvez forcer l'interpréteur à rechercher dans un répertoire différent les modules appartenant à ce package.
Cela vous permettrait, par exemple, de charger différentes versions du même module en fonction des conditions d'exécution. Vous pouvez le faire si vous souhaitez utiliser différentes implémentations de la même fonctionnalité sur différentes plates-formes.
En plus de sélectionner différentes versions d'un module en fonction des conditions d'exécution comme Syntactic dit, cette fonctionnalité vous permettra également de diviser votre package en plusieurs pièces/téléchargements/installations tout en conservant l'apparence d'une seule logique paquet.
Considérer ce qui suit.
mypkg
et _mypkg_foo
._mypkg_foo
contient un module optionnel pour mypkg
, foo.py
.mypkg
ne contient pas de foo.py
.mypkg
's __init__.py
peut faire quelque chose comme ça:
try:
import _mypkg_foo
__path__.append(os.path.abspath(os.path.dirname(_mypkg_foo.__file__)))
import mypkg.foo
except ImportError:
pass
Si quelqu'un a installé le package _mypkg_foo
, puis mypkg.foo
est à leur disposition. S'ils ne l'ont pas fait, cela n'existe pas.
Une situation particulière que j'ai rencontrée est lorsqu'un package devient suffisamment grand pour que je veuille en diviser des parties en sous-répertoires sans avoir à modifier le code qui le référence.
Par exemple, j'ai un package appelé views
qui collectait un certain nombre de fonctions utilitaires de support qui se confondaient avec l'objectif principal de niveau supérieur du package. J'ai pu déplacer ces fonctions de support dans un sous-répertoire utils
et ajouter la ligne suivante au __init__.py
pour le package views
:
__path__.append(os.path.join(os.path.dirname(__file__), "utils"))
Avec ce changement aussi views/__init_.py
, J'ai pu exécuter le reste du logiciel avec la nouvelle structure de fichiers sans aucune autre modification des fichiers.
(J'ai essayé de faire quelque chose de similaire avec les instructions import
dans les views/__init__.py
fichier, mais les modules du sous-package n'étaient toujours pas visibles via une importation du package view
- je ne suis pas tout à fait sûr de manquer quelque chose; commentaires sur cet accueil!)
(Cette réponse basée sur Python 2.7)