Parfois, il devient nécessaire de séparer plusieurs packages situés dans le même espace de noms le long de différents chemins physiques. Par exemple, si vous souhaitez pouvoir transférer différentes dispositions de plugins, avoir la possibilité de les ajouter ultérieurement sans contrôler leur emplacement et, en même temps, d'y accéder via le même espace de noms.
Cette feuille de triche, qui convient mieux aux débutants, est dédiée aux espaces de noms Python.
Voyons comment cela peut être fait dans différentes versions de Python, car bien que Python2 ne soit plus pris en charge bientôt, beaucoup d'entre nous sont actuellement entre deux incendies, et ce n'est là qu'une des nuances importantes de la transition.

Considérez cet exemple:
Nous voulons obtenir la structure du package:
namespace1 package1 module1 package2 module2
Contenu du fichier Module1
print('package 1') var1 = 1
Contenu du fichier Module2
print('package 2') var2 = 2
Dans le même temps, les packages sont distribués dans la structure de dossiers suivante:
path1 namespace1 package1 module1 path2 namespace1 package2 module2
Supposons que path1 et path2 soient déjà ajoutés à sys.path. Nous devons accéder au module1 et au module2:
from namespace1.package1 import module1 from namespace1.package2 import module2
Que se passe-t-il dans
Python 3.7 lorsque ce code est exécuté? Tout fonctionne à merveille:
package 1 package 2
Avec PEP-420 en Python 3.3, la prise en charge des espaces de noms implicites est apparue. De plus, lors de l'importation d'un package à partir de py33, vous n'avez pas besoin de créer de fichiers __init__.py. Et lors de l'importation d'espace de noms, c'est simplement _ interdit_. Si le fichier __init__.py est présent dans un ou les deux répertoires portant le nom namespace1, une erreur se produit lors de l'importation du deuxième package.
ModuleNotFoundError: No module named 'namespace1.package2'
Ainsi, la présence d'un initié détermine explicitement le package, et les packages ne peuvent pas être combinés, il s'agit d'une seule entité. Si vous démarrez un nouveau projet, indépendamment des anciennes conceptions, et que les packages seront installés à l'aide de pip, alors c'est la façon de s'en tenir. Cependant, nous héritons parfois de l'ancien code, qui doit également être conservé, au moins pendant un certain temps, ou porté sur une nouvelle version.
Passons à
Python 2.7 . Avec cette version, c'est déjà plus intéressant, vous devez d'abord ajouter __init__.py à chaque répertoire pour créer des packages, sinon l'interpréteur ne reconnaît tout simplement pas le package dans cet ensemble de fichiers. Ensuite, écrivez une déclaration d'espace de noms explicite dans les fichiers __init__ liés à namespace1, sinon, seul le premier package sera importé.
from pkgutil import extend_path __path__ = extend_path(__path__, __name__)
Que se passe-t-il avec ça? Lorsque l'interpréteur atteint la première importation, un package du même nom est recherché dans sys.path, il se trouve dans path1 / namespace1 et l'interpréteur exécute path1 / namespace1 / __ init__.py. Aucune autre recherche n'est effectuée. Cependant, la fonction extend_path elle-même effectue une recherche sur sys.path, recherche tous les packages portant le nom namespace1 et le nom interne et les ajoute à la variable __path__ du package namespace1, qui est utilisée pour rechercher des packages enfants dans cet espace de noms.
Dans les guides officiels, il est recommandé que les initiales soient les mêmes à chaque fois que namespace1 est placé. En fait, ils peuvent être vides tous sauf le premier, qui se trouve lors d'une recherche dans sys.path, dans lequel pkgutil.extend_path doit être appelé, car les autres ne sont pas exécutés. Cependant, bien sûr, il vaut mieux que l'appel soit vraiment dans chaque équipe, afin de ne pas lier votre logique «au cas où» et de ne pas deviner quelle équipe a été la première à exécuter, car l'ordre de recherche peut changer. Pour la même raison, vous ne devez pas placer d'autres fichiers __init__ logiques dans la zone variable.
Cela fonctionnera dans les versions futures et ce code peut être utilisé pour écrire du
code compatible , mais gardez à l'esprit que vous devez respecter la méthode choisie dans chaque package distribué. Si, dans la version 3, vous mettez une boîte de réception dans certains packages lors d'un appel à pkgutil.extend_path et en laissez certains sans boîte de réception, cela ne fonctionnera pas.
En outre, cette option convient également au cas où vous prévoyez d'installer à l'aide de l'installation de python setup.py.
Une autre façon, qui est maintenant considérée comme quelque peu dépassée, mais on la trouve encore beaucoup où:
Le module pkg_resources est livré avec le package setuptools. Ici, la signification est la même que dans pkgutil - il est nécessaire que chaque fichier __init__ à chaque emplacement de namespace1 contienne la même déclaration d'espace de noms et qu'il n'y ait pas d'autre code. Dans le même temps, il est nécessaire d'enregistrer l'espace de noms namespace_packages = ['namespace1'] dans setup.py. Une description plus détaillée de la création de packages dépasse le cadre de cet article.
De plus, vous pouvez souvent trouver un tel code
try: __import__('pkg_resources').declare_namespace(__name__) except: from pkgutil import extend_path __path__ = extend_path(__path__, __name__)
Ici, la logique est simple - si setuptools n'est pas installé, alors nous utilisons pkgutil, qui est inclus dans la bibliothèque standard.
Si vous configurez un espace de noms de l'une de ces manières, vous pouvez en appeler un autre à partir d'un module. Par exemple, changez namespace1 / package2 / module2
import namespace1.package1.module1 print(var1)
Et puis nous verrons ce qui se passe si nous nommons par erreur un nouveau paquet ainsi qu'un existant et l'enveloppons avec le même espace de noms. Par exemple, il y aura deux packages à différents endroits avec le nom package1.
namespace1 package1 module1 package1 module2
Dans ce cas, seul le premier sera importé et il n'y aura pas d'accès au module2. Les packages ne peuvent pas être combinés.
from namespace1.package1 import module1 from namespace1.package1 import module2
Résumé:- Pour Python antérieur à 3.3 et l'installation avec pip, il est recommandé d'utiliser une déclaration d'espace de noms implicite.
- En cas de prise en charge des versions 2 et 3, ainsi que d'une installation avec à la fois l'installation de pip et python setup.py, l'option avec pkgutil est recommandée.
- L'option pkg_resources est recommandée si vous devez prendre en charge des packages plus anciens à l'aide de cette méthode, ou si le package doit être protégé par zip.
Sources:
Des exemples peuvent être trouvés
ici .