J'ai un fichier appelé foobar (sans extension .py). Dans le même répertoire, j'ai un autre fichier python qui essaie de l'importer:
import foobar
Mais cela ne fonctionne que si je renomme le fichier en foobar.py. Est-il possible d'importer un module python qui n'a pas l'extension .py?
Mise à jour: le fichier n'a pas d'extension car je l'utilise également comme script autonome, et je ne veux pas taper l'extension .py pour l'exécuter.
Update2: Je vais opter pour la solution de lien symbolique mentionnée ci-dessous.
Vous pouvez utiliser le imp.load_source
fonction (du module imp
), pour charger dynamiquement un module à partir d'un chemin de système de fichiers donné.
foobar = imp.load_source('foobar', '/path/to/foobar')
Cette discussion SO montre également quelques options intéressantes.
Comme d'autres l'ont mentionné, vous pouvez utiliser imp.load_source, mais cela rendra votre code plus difficile à lire. Je ne le recommanderais vraiment que si vous devez importer des modules dont les noms ou les chemins ne sont connus qu'au moment de l'exécution.
Quelle est la raison pour laquelle vous ne souhaitez pas utiliser l'extension .py? Le cas le plus courant pour ne pas vouloir utiliser l'extension .py est que le script python est également exécuté en tant qu'exécutable, mais vous voulez toujours que d'autres modules puissent l'importer. Si cela Dans ce cas, il peut être avantageux de déplacer des fonctionnalités dans un fichier .py portant un nom similaire, puis d'utiliser foobar
comme wrapper.
imp.load_source(module_name, path)
devrait faire ou vous pouvez faire la route imp.load_module(module_name, file_handle, ...)
plus verbeuse si vous avez un handle de fichier à la place
Voici une solution pour Python 3.4+:
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
spec = spec_from_loader("foobar", SourceFileLoader("foobar", "/path/to/foobar"))
foobar = module_from_spec(spec)
spec.loader.exec_module(foobar)
En utilisant spec_from_loader
et en spécifiant explicitement un SourceFileLoader
forcera le machinerie à charger le fichier en tant que source, sans essayer de comprendre le type du fichier de l'extension. Cela signifie que vous pouvez charger le fichier même s'il n'est pas répertorié dans importlib.machinery.SOURCE_SUFFIXES
.
Si vous souhaitez continuer à importer le fichier par nom après le premier chargement, ajoutez le module à sys.modules
:
sys.modules['foobar'] = foobar
Si vous installez le script avec le gestionnaire de paquets (deb ou similaire), une autre option serait d'utiliser setuptools:
"... il n'y a pas de moyen simple pour que le nom de fichier d'un script corresponde aux conventions locales sur les plates-formes Windows et POSIX. Pour un autre, vous devez souvent créer un fichier séparé juste pour le script" principal ", lorsque votre" principal "réel est un fonctionner dans un module quelque part ... setuptools corrige tous ces problèmes en générant automatiquement des scripts pour vous avec l'extension correcte, et sur Windows, il créera même un fichier .exe ... "
https://pythonhosted.org/setuptools/setuptools.html#automatic-script-creation
importlib
fonction d'assistance
Voici un assistant pratique et prêt à l'emploi pour remplacer imp
, par un exemple, basé sur ce qui a été mentionné à: https://stackoverflow.com/a/43602645/895245 =
main.py
#!/usr/bin/env python3
import os
import importlib
def import_path(path):
module_name = os.path.basename(path).replace('-', '_')
spec = importlib.util.spec_from_loader(
module_name,
importlib.machinery.SourceFileLoader(module_name, path)
)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
sys.modules[module_name] = module
return module
notmain = import_path('not-main')
print(notmain)
print(notmain.x)
pas-principal
x = 1
Courir:
python3 main.py
Production:
<module 'not_main' from 'not-main'>
1
Je remplace -
avec _
parce que mes exécutables importables Python sans extension ont des tirets. Ce n'est pas obligatoire, mais produit de meilleurs noms de module.
Ce modèle est également mentionné dans les documents à: https://docs.python.org/3.7/library/importlib.html#importing-a-source-file-directly
J'ai fini par y aller car après la mise à jour vers Python 3.7, import imp
imprime:
DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
et je ne sais pas comment désactiver cela.
Testé en Python 3.7.3.