Je veux hériter d'une classe dans un fichier qui se trouve dans un répertoire au-dessus du répertoire actuel.
Est-il possible d'importer relativement ce fichier?
Je veux hériter d'une classe dans un fichier qui se trouve dans un répertoire au-dessus du répertoire actuel.
Est-il possible d'importer relativement ce fichier?
Réponses:
from ..subpkg2 import mod
Selon les documents Python: lorsque vous êtes dans une hiérarchie de packages, utilisez deux points, comme le dit la déclaration d'importation doc:
Lorsque vous spécifiez le module à importer, vous n'avez pas besoin de spécifier le nom absolu du module. Lorsqu'un module ou un package est contenu dans un autre package, il est possible d'effectuer une importation relative dans le même package supérieur sans avoir à mentionner le nom du package. En utilisant des points de début dans le module ou le package spécifié après,
from
vous pouvez spécifier la hauteur à parcourir dans la hiérarchie de packages actuelle sans spécifier de noms exacts. Un point en tête signifie le package actuel où le module effectuant l'importation existe. Deux points signifient un niveau de package . Trois points représentent deux niveaux, etc. Donc, si vous exécutez àfrom . import mod
partir d'un module dupkg
package, vous finirez par importerpkg.mod
. Si vous exécutezfrom ..subpkg2 import mod
de l'intérieur,pkg.subpkg1
vous importerezpkg.subpkg2.mod
. La spécification des importations relatives est contenue dans le PEP 328 .
Le PEP 328 traite des importations absolues / relatives.
import sys
sys.path.append("..") # Adds higher directory to python modules path.
La réponse de @ gimel est correcte si vous pouvez garantir la hiérarchie des packages qu'il mentionne. Si vous ne pouvez pas - si votre besoin réel est tel que vous l'avez exprimé, exclusivement lié aux répertoires et sans aucune relation nécessaire avec l'empaquetage - alors vous devez travailler __file__
pour trouver le répertoire parent (quelques os.path.dirname
appels suffiront; -), puis (si ce répertoire n'est pas déjà sys.path
activé) ajoutez provisoirement ledit répertoire au tout début de sys.path
, __import__
supprimez à nouveau ledit répertoire - un travail compliqué en effet, mais, "quand vous le devez, vous devez" (et Pyhon s'efforce de n'arrêtez jamais le programmeur de faire ce qui doit être fait - comme le dit la norme ISO C dans la section "Spirit of C" dans sa préface! -).
Voici un exemple qui peut vous convenir:
import sys
import os.path
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
import module_in_parent_dir
sys.path
rendre ainsi le même module disponible sous des noms différents et tous les bogues correspondants. autopath.py dans pypy ou _preamble.py dans twisted le résoudre en utilisant un critère de recherche qui identifie le package de niveau supérieur tout en parcourant les répertoires vers le haut.
sys.path.remove(pathYouJustAdded)
après l'importation dont vous aviez besoin afin de ne pas conserver ce nouveau chemin.
Importez le module à partir d'un répertoire qui se trouve exactement un niveau au-dessus du répertoire actuel:
from .. import module
from .. import module
j'ai obtenu l'erreur ValueError: tentative d'importation relative dans un non-package en suivant la recommandation
préface: J'ai fait une réécriture substantielle d'une réponse précédente dans l'espoir d'aider les gens à pénétrer l'écosystème de python et, espérons-le, de donner à chacun le meilleur changement de succès avec le système d'importation de python.
Cela couvrira les importations relatives au sein d'un package , ce qui, je pense, est le cas le plus probable de la question d'OP.
C'est pourquoi nous écrivons import foo
pour charger un module "foo" depuis l'espace de noms racine, au lieu d'écrire:
foo = dict(); # please avoid doing this
with open(os.path.join(os.path.dirname(__file__), '../foo.py') as foo_fh: # please avoid doing this
exec(compile(foo_fh.read(), 'foo.py', 'exec'), foo) # please avoid doing this
C'est pourquoi nous pouvons intégrer python dans un environnement où il n'y a pas de système de fichiers de facto sans en fournir un virtuel, tel que Jython.
Le découplage d'un système de fichiers permet aux importations d'être flexibles, cette conception permet des choses comme les importations à partir de fichiers archive / zip, l'importation de singletons, la mise en cache de bytecode, les extensions cffi, même le chargement de définition de code à distance.
Donc, si les importations ne sont pas couplées à un système de fichiers, que signifie "un répertoire vers le haut"? Nous devons sélectionner quelques heuristiques mais nous pouvons le faire, par exemple lorsque vous travaillez dans un package , certaines heuristiques ont déjà été définies qui rendent les importations relatives similaires .foo
et..foo
fonctionnent dans le même package. Cool!
Si vous souhaitez sincèrement coupler vos modèles de chargement de code source à un système de fichiers, vous pouvez le faire. Vous devrez choisir votre propre heuristique et utiliser une sorte de machinerie d'importation, je recommande importlib
L'exemple d'importlib de Python ressemble à ceci:
import importlib.util
import sys
# For illustrative purposes.
file_path = os.path.join(os.path.dirname(__file__), '../foo.py')
module_name = 'foo'
foo_spec = importlib.util.spec_from_file_location(module_name, file_path)
# foo_spec is a ModuleSpec specifying a SourceFileLoader
foo_module = importlib.util.module_from_spec(foo_spec)
sys.modules[module_name] = foo_module
foo_spec.loader.exec_module(foo_module)
foo = sys.modules[module_name]
# foo is the sys.modules['foo'] singleton
Il y a un grand exemple de projet disponible officiellement ici: https://github.com/pypa/sampleproject
Un package python est une collection d'informations sur votre code source, qui peut indiquer à d'autres outils comment copier votre code source sur d'autres ordinateurs et comment intégrer votre code source dans le chemin d'accès de ce système afin qu'il import foo
fonctionne pour d'autres ordinateurs (quel que soit l'interpréteur, système d'exploitation hôte, etc.)
Permet d'avoir un nom de package foo
, dans un répertoire (de préférence un répertoire vide).
some_directory/
foo.py # `if __name__ == "__main__":` lives here
Ma préférence est de créer en setup.py
tant que frère foo.py
, car cela rend l'écriture du fichier setup.py plus simple, mais vous pouvez écrire la configuration pour changer / rediriger tout ce que setuptools fait par défaut si vous le souhaitez; par exemple, placer foo.py
sous un répertoire "src /" est quelque peu populaire, non couvert ici.
some_directory/
foo.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
py_modules=['foo'],
)
.
python3 -m pip install --editable ./ # or path/to/some_directory/
"editable" aka -e
redirigera à nouveau la machine d'importation pour charger les fichiers source dans ce répertoire, au lieu de copier les fichiers exacts actuels dans la bibliothèque de l'environnement d'installation. Cela peut également provoquer des différences de comportement sur la machine d'un développeur, assurez-vous de tester votre code! Il existe des outils autres que pip, mais je recommanderais pip comme introduction :)
J'aime aussi faire foo
un "package" (un répertoire contenant __init__.py
) au lieu d'un module (un seul fichier ".py"), les "packages" et les "modules" peuvent être chargés dans l'espace de noms racine, les modules permettent des espaces de noms imbriqués, ce qui est utile si nous voulons avoir une importation "relative d'un répertoire vers le haut".
some_directory/
foo/
__init__.py
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
)
J'aime aussi faire un foo/__main__.py
, cela permet à python d'exécuter le package en tant que module, par exemple python3 -m foo
s'exécutera en foo/__main__.py
tant que __main__
.
some_directory/
foo/
__init__.py
__main__.py # `if __name__ == "__main__":` lives here, `def main():` too!
setup.py
.
#!/usr/bin/env python3
# setup.py
import setuptools
setuptools.setup(
name="foo",
...
packages=['foo'],
...
entry_points={
'console_scripts': [
# "foo" will be added to the installing-environment's text mode shell, eg `bash -c foo`
'foo=foo.__main__:main',
]
},
)
Permet d'étoffer cela avec quelques modules supplémentaires: Fondamentalement, vous pouvez avoir une structure de répertoire comme ceci:
some_directory/
bar.py # `import bar`
foo/
__init__.py # `import foo`
__main__.py
baz.py # `import foo.baz
spam/
__init__.py # `import foo.spam`
eggs.py # `import foo.spam.eggs`
setup.py
setup.py
contient classiquement des informations de métadonnées sur le code source, telles que:
foo
, bien que le remplacement des traits de soulignement par des tirets soit populairepython ./setup.py test
Son très vaste, il peut même compiler des extensions c à la volée si un module source est installé sur une machine de développement. Pour un exemple quotidien, je recommande setup.py du référentiel d'échantillons PYPA.
Si vous publiez un artefact de génération, par exemple une copie du code destiné à exécuter des ordinateurs presque identiques, un fichier requirements.txt est un moyen populaire de prendre des instantanés des informations de dépendance exactes, où "install_requires" est un bon moyen de capturer le minimum et versions compatibles maximum. Cependant, étant donné que les machines cibles sont de toute façon presque identiques, je recommande fortement de créer un tarball d'un préfixe python entier. Cela peut être délicat, trop détaillé pour entrer ici. Consultez pip install
l' --target
option de, ou virtualenv aka venv pour les pistes.
retour à l'exemple
De foo / spam / eggs.py, si nous voulions du code de foo / baz, nous pourrions le demander par son espace de noms absolu:
import foo.baz
Si nous voulions réserver la capacité de déplacer eggs.py dans un autre répertoire à l'avenir avec une autre baz
implémentation relative , nous pourrions utiliser une importation relative comme:
import ..baz
Pour charger le code python de manière fiable, ayez ce code dans un module et ce module installé dans la bibliothèque de python.
Les modules installés peuvent toujours être chargés à partir de l'espace de noms de niveau supérieur avec import <name>
Il y a un grand exemple de projet disponible officiellement ici: https://github.com/pypa/sampleproject
Fondamentalement, vous pouvez avoir une structure de répertoires comme celle-ci:
the_foo_project/
setup.py
bar.py # `import bar`
foo/
__init__.py # `import foo`
baz.py # `import foo.baz`
faz/ # `import foo.faz`
__init__.py
daz.py # `import foo.faz.daz` ... etc.
.
Assurez - vous de déclarer votre setuptools.setup()
dans setup.py
,
exemple officiel: https://github.com/pypa/sampleproject/blob/master/setup.py
Dans notre cas, nous voulons probablement exporter bar.py
et foo/__init__.py
, mon bref exemple:
#!/usr/bin/env python3
import setuptools
setuptools.setup(
...
py_modules=['bar'],
packages=['foo'],
...
entry_points={},
# Note, any changes to your setup.py, like adding to `packages`, or
# changing `entry_points` will require the module to be reinstalled;
# `python3 -m pip install --upgrade --editable ./the_foo_project
)
.
Nous pouvons maintenant installer notre module dans la bibliothèque python; avec pip, vous pouvez installer the_foo_project
dans votre bibliothèque python en mode édition, afin que nous puissions y travailler en temps réel
python3 -m pip install --editable=./the_foo_project
# if you get a permission error, you can always use
# `pip ... --user` to install in your user python library
.
Maintenant, à partir de n'importe quel contexte python, nous pouvons charger nos py_modules et packages partagés
#!/usr/bin/env python3
import bar
import foo
print(dir(bar))
print(dir(foo))
pip install --edit foo
, presque toujours à l'intérieur d'un virtualenv. Je n'écris presque jamais un module qui n'est pas destiné à être installé. si je comprends mal quelque chose que j'aimerais savoir.
editable
les liens d'oeufs et les modules python installés ne sont pas exactement les mêmes à tous points de vue; par exemple, l'ajout d'un nouvel espace de noms à un module modifiable se trouvera dans votre recherche de chemin, mais s'il n'est pas exporté dans votre fichier setup.py, il ne sera pas empaqueté / installé! Testez votre cas d'utilisation :)