Wie importiere ich ein Modul mit dem vollständigen Pfad?

1139

Wie kann ich ein Python-Modul mit seinem vollständigen Pfad laden? Beachten Sie, dass sich die Datei an einer beliebigen Stelle im Dateisystem befinden kann, da es sich um eine Konfigurationsoption handelt.

derfred
quelle
21
Schöne und einfache Frage - und nützliche Antworten, aber sie lassen mich fragen, was mit dem Python-Mantra "Es gibt einen offensichtlichen Weg" passiert ist . Es scheint nicht wie eine einzelne oder eine einfache und offensichtliche Antwort darauf. Scheint lächerlich hackig und versionabhängig für solch eine grundlegende Operation (und es sieht in neueren Versionen aufgeblähter aus ..).
Inger

Antworten:

1281

Für Python 3.5+ verwenden Sie:

import importlib.util
spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
foo.MyClass()

Verwenden Sie für Python 3.3 und 3.4:

from importlib.machinery import SourceFileLoader

foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()

(Obwohl dies in Python 3.4 veraltet ist.)

Für Python 2 verwenden Sie:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

Es gibt äquivalente Komfortfunktionen für kompilierte Python-Dateien und DLLs.

Siehe auch http://bugs.python.org/issue21436 .

Sebastian Rittau
quelle
53
Wenn ich den Namespace 'module.name' kennen würde, würde ich ihn bereits verwenden __import__.
Sridhar Ratnakumar
62
@SridharRatnakumar Der Wert des ersten Arguments von imp.load_sourcesetzt nur den Wert des .__name__zurückgegebenen Moduls. Das Laden wird nicht beeinflusst.
Dan D.
17
@ DanD. - Das erste Argument von imp.load_source()bestimmt den Schlüssel des neuen Eintrags, der im sys.modulesWörterbuch erstellt wurde, sodass das erste Argument tatsächlich das Laden beeinflusst.
Brandon Rhodes
9
Das impModul ist seit Version 3.4 veraltet: Für das impPaket steht die Verwerfung zugunsten von aus importlib.
Chiel zehn Brinke
9
@AXO und mehr auf den Punkt fragt man sich , warum etwas so einfach und einfach , wie dies hat so kompliziert sein. Es ist nicht in vielen anderen Sprachen.
Rocky
422

Das Hinzufügen eines Pfads zu sys.path (gegenüber der Verwendung von imp) hat den Vorteil, dass beim Importieren von mehr als einem Modul aus einem einzelnen Paket die Arbeit vereinfacht wird. Zum Beispiel:

import sys
# the mock-0.3.1 dir contains testcase.py, testutils.py & mock.py
sys.path.append('/foo/bar/mock-0.3.1')

from testcase import TestCase
from testutils import RunTests
from mock import Mock, sentinel, patch
Daryl Spitzer
quelle
13
Wie sys.path.appendverweisen wir auf eine einzelne Python-Datei anstelle eines Verzeichnisses?
Phani
28
:-) Vielleicht ist Ihre Frage besser als StackOverflow-Frage geeignet, nicht als Kommentar zu einer Antwort.
Daryl Spitzer
3
Der Python-Pfad kann Zip-Archive, "Eier" (eine komplexe Art von Zip-Archiven) usw. enthalten. Module können aus diesen importiert werden. Die Pfadelemente sind zwar Container von Dateien, aber nicht unbedingt Verzeichnisse.
Alexis
12
Beachten Sie, dass Python Importanweisungen zwischenspeichert. In dem seltenen Fall, dass Sie zwei verschiedene Ordner haben, die einen einzelnen Klassennamen (classX) gemeinsam nutzen, funktioniert der Ansatz, einen Pfad zu sys.path hinzuzufügen, classX zu importieren, den Pfad zu entfernen und die verbleibenden Pfade zu wiederholen, nicht. Python lädt die Klasse immer vom ersten Pfad aus ihrem Cache. In meinem Fall wollte ich ein Plugin-System erstellen, in dem alle Plugins eine bestimmte Klasse X implementieren. Am Ende habe ich SourceFileLoader verwendet . Beachten Sie, dass die Ablehnung umstritten ist .
ComFreek
3
Beachten Sie, dass dieser Ansatz es dem importierten Modul ermöglicht, andere Module aus demselben Verzeichnis zu importieren, was Module häufig tun, während der Ansatz der akzeptierten Antwort dies nicht tut (zumindest bei 3.7). importlib.import_module(mod_name)kann hier anstelle des expliziten Imports verwendet werden, wenn der Modulname zur Laufzeit nicht bekannt ist. Am sys.path.pop()Ende würde ich jedoch ein hinzufügen , vorausgesetzt, der importierte Code versucht nicht, mehr Module zu importieren, während er verwendet wird.
Eli_B
43

Wenn Ihr Top-Level-Modul keine Datei ist, sondern als Verzeichnis mit __init__.py gepackt ist, funktioniert die akzeptierte Lösung fast, aber nicht ganz. In Python 3.5+ wird der folgende Code benötigt (beachten Sie die hinzugefügte Zeile, die mit 'sys.modules' beginnt):

MODULE_PATH = "/path/to/your/module/__init__.py"
MODULE_NAME = "mymodule"
import importlib
import sys
spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module 
spec.loader.exec_module(module)

Ohne diese Zeile wird bei der Ausführung von exec_module versucht, relative Importe in Ihrer obersten Ebene __init__.py an den Modulnamen der obersten Ebene zu binden - in diesem Fall "mymodule". "Mymodule" ist jedoch noch nicht geladen, sodass die Fehlermeldung "SystemError: Übergeordnetes Modul 'mymodule' nicht geladen, relativer Import kann nicht ausgeführt werden" angezeigt wird. Sie müssen den Namen also binden, bevor Sie ihn laden. Der Grund dafür ist die grundlegende Invariante des relativen Importsystems: "Die invariante Position ist, dass Sie sys.modules ['spam'] und sys.modules ['spam.foo'] haben (wie Sie es nach dem obigen Import tun würden ) muss letzteres als foo-Attribut des ersteren erscheinen " wie hier diskutiert .

Sam Grondahl
quelle
Vielen Dank! Diese Methode ermöglicht relative Importe zwischen Submodulen. Großartig!
Tebanep
Diese Antwort entspricht der Dokumentation hier: docs.python.org/3/library/… .
Tim Ludwinski
1
aber was ist mymodule?
Gulzar
@ Gulzar, es ist der Name, den Sie Ihrem Modul geben möchten, damit Sie später Folgendes tun können: "from mymodule import myclass"
Idodo
Also ... /path/to/your/module/ist eigentlich /path/to/your/PACKAGE/? und damit mymodulemeinst du myfile.py?
Gulzar
37

Um Ihr Modul zu importieren, müssen Sie sein Verzeichnis vorübergehend oder dauerhaft zur Umgebungsvariablen hinzufügen.

Vorübergehend

import sys
sys.path.append("/path/to/my/modules/")
import my_module

Permanent

Hinzufügen der folgenden Zeile zu Ihrer .bashrcDatei (unter Linux) und Ausführen source ~/.bashrcim Terminal:

export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"

Kredit / Quelle: saarrrr , eine weitere Frage zum Stapelaustausch

Miladiouss
quelle
3
Diese "temporäre" Lösung ist eine großartige Antwort, wenn Sie ein Projekt in einem Jupyter-Notizbuch an anderer Stelle ausführen möchten.
Fordy
Aber ... es ist gefährlich, den Weg zu manipulieren
Shai Alon
@ShaiAlon Sie fügen Pfade hinzu. Wenn Sie also Codes von einem Computer auf einen anderen übertragen, besteht keine Gefahr, dass Pfade durcheinander geraten. Für die Paketentwicklung importiere ich also nur lokale Pakete. Außerdem sollten Paketnamen eindeutig sein. Wenn Sie sich Sorgen machen, verwenden Sie die temporäre Lösung.
Miladiouss
28

Es hört sich so an, als ob Sie die Konfigurationsdatei nicht speziell importieren möchten (was viele Nebenwirkungen und zusätzliche Komplikationen mit sich bringt), sondern sie nur ausführen und auf den resultierenden Namespace zugreifen möchten. Die Standardbibliothek bietet eine spezielle API in Form von runpy.run_path :

from runpy import run_path
settings = run_path("/path/to/file.py")

Diese Schnittstelle ist in Python 2.7 und Python 3.2+ verfügbar

ncoghlan
quelle
Ich mag diese Methode, aber wenn ich das Ergebnis von run_path erhalte, ist es ein Wörterbuch, auf das ich scheinbar nicht zugreifen kann?
Stephen Ellwood
Was meinst du mit "kann nicht zugreifen"? Sie können nicht davon importieren (das ist , warum dies nur eine gute Option, wenn Import-Stil Zugang nicht tatsächlich erforderlich ist ), aber der Inhalt sollte über die reguläre dict API (verfügbar sein result[name], result.get('name', default_value)etc)
ncoghlan
Diese Antwort wird viel unterschätzt. Es ist sehr kurz und einfach! Noch besser, wenn Sie einen richtigen Modul-Namespace benötigen, können Sie from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
Folgendes
20

Sie können auch so etwas tun und das Verzeichnis, in dem sich die Konfigurationsdatei befindet, zum Python-Ladepfad hinzufügen und dann einfach einen normalen Import durchführen, vorausgesetzt, Sie kennen den Namen der Datei im Voraus, in diesem Fall "config".

Chaotisch, aber es funktioniert.

configfile = '~/config.py'

import os
import sys

sys.path.append(os.path.dirname(os.path.expanduser(configfile)))

import config
ctcherry
quelle
Das ist nicht dynamisch.
Shai Alon
Ich habe versucht: config_file = 'setup-for-chats', setup_file = get_setup_file (config_file + ".py"), sys.path.append (os.path.dirname (os.path.expanduser (setup_file))), import config_file >> "ImportError: Kein Modul namens config_file"
Shai Alon
17

Du kannst den ... benutzen

load_source(module_name, path_to_file) 

Methode aus imp Modul .

zuber
quelle
... und imp.load_dynamic(module_name, path_to_file)für DLLs
HEKTO
34
Kopf hoch, dass Kobold jetzt veraltet ist.
t1m0
13
def import_file(full_path_to_module):
    try:
        import os
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)
        save_cwd = os.getcwd()
        os.chdir(module_dir)
        module_obj = __import__(module_name)
        module_obj.__file__ = full_path_to_module
        globals()[module_name] = module_obj
        os.chdir(save_cwd)
    except:
        raise ImportError

import_file('/home/somebody/somemodule.py')
Chris Calloway
quelle
37
Warum 14 Zeilen Buggy-Code schreiben, wenn dies bereits von der Standardbibliothek behandelt wird? Sie haben keine Fehlerprüfung für Format oder Inhalt von full_path_to_module oder den Operationen os.whatever durchgeführt. und die Verwendung einer except:Sammelklausel ist selten eine gute Idee.
Chris Johnson
Sie sollten hier mehr "try-finally" verwenden. ZBsave_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
Kay - SE ist böse
11
@ChrisJohnson this is already addressed by the standard libraryJa, aber Python hat die böse Angewohnheit, nicht abwärtskompatibel zu sein ... wie die überprüfte Antwort besagt, gibt es vor und nach 3.3 zwei verschiedene Möglichkeiten. In diesem Fall möchte ich lieber meine eigene universelle Funktion schreiben, als die Version im laufenden Betrieb zu überprüfen. Und ja, vielleicht ist dieser Code nicht allzu gut fehlergeschützt, aber er zeigt eine Idee (os.chdir (), über die ich noch nicht nachgedacht habe), auf deren Grundlage ich einen besseren Code schreiben kann. Daher +1.
Sushi271
13

Hier ist ein Code, der in allen Python-Versionen von 2.7-3.5 und wahrscheinlich sogar in anderen funktioniert.

config_file = "/tmp/config.py"
with open(config_file) as f:
    code = compile(f.read(), config_file, 'exec')
    exec(code, globals(), locals())

Ich habe es getestet. Es mag hässlich sein, aber bisher ist es das einzige, das in allen Versionen funktioniert.

Sorin
quelle
1
Diese Antwort hat bei mir load_sourcenicht funktioniert, da sie das Skript importiert und dem Skript den Zugriff auf die Module und Globals zum Zeitpunkt des Imports ermöglicht.
Klik
13

Ich habe eine leicht modifizierte Version von @ SebastianRittaus wunderbarer Antwort (für Python> 3.4, glaube ich) entwickelt, mit der Sie eine Datei mit einer beliebigen Erweiterung als Modul laden können, spec_from_loaderanstatt spec_from_file_location:

from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader 

spec = spec_from_loader("module.name", SourceFileLoader("module.name", "/path/to/file.py"))
mod = module_from_spec(spec)
spec.loader.exec_module(mod)

Der Vorteil der expliziten Codierung des Pfads SourceFileLoaderbesteht darin, dass die Maschine nicht versucht, den Dateityp aus der Erweiterung herauszufinden. Dies bedeutet, dass Sie .txtmit dieser Methode so etwas wie eine Datei laden können , dies jedoch nicht spec_from_file_locationohne Angabe des Loaders tun können, da dieser .txtnicht vorhanden ist importlib.machinery.SOURCE_SUFFIXES.

Verrückter Physiker
quelle
13

Meinst du laden oder importieren?

Sie können die sys.pathListe bearbeiten, den Pfad zu Ihrem Modul angeben und dann Ihr Modul importieren. Beispiel: Ein Modul unter:

/foo/bar.py

Du könntest es tun:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
Weizen
quelle
1
@Wheat Warum sys.path [0: 0] anstelle von sys.path [0]?
user618677
5
B / c sys.path [0] = xy überschreibt das erste Pfadelement, während Pfad [0: 0] = xy äquivalent zu path.insert (0, xy) ist
dom0
2
hm das path.insert hat bei mir funktioniert, der [0: 0] Trick jedoch nicht.
Jsh
11
sys.path[0:0] = ['/foo']
Kevin Edwards
6
Explicit is better than implicit.Warum also nicht sys.path.insert(0, ...)statt sys.path[0:0]?
Winklerrr
8

Ich glaube, Sie können das angegebene Modul verwenden imp.find_module()und imp.load_module()laden. Sie müssen den Modulnamen vom Pfad trennen, dh wenn Sie laden /home/mypath/mymodule.pymöchten, müssen Sie Folgendes tun:

imp.find_module('mymodule', '/home/mypath/')

... aber das sollte den Job erledigen.

Matt
quelle
6

Sie können das pkgutilModul (insbesondere die walk_packagesMethode) verwenden, um eine Liste der Pakete im aktuellen Verzeichnis abzurufen. Von dort aus ist es trivial, die importlibgewünschten Module mithilfe der Maschinen zu importieren:

import pkgutil
import importlib

packages = pkgutil.walk_packages(path='.')
for importer, name, is_package in packages:
    mod = importlib.import_module(name)
    # do whatever you want with module now, it's been imported!
bob_twinkles
quelle
5

Erstellen Sie das Python-Modul test.py

import sys
sys.path.append("<project-path>/lib/")
from tes1 import Client1
from tes2 import Client2
import tes3

Erstellen Sie das Python-Modul test_check.py

from test import Client1
from test import Client2
from test import test3

Wir können das importierte Modul aus dem Modul importieren.

abhimanyu
quelle
4

Dieser Bereich von Python 3.4 scheint äußerst schwierig zu verstehen zu sein! Mit ein bisschen Hacking mit dem Code von Chris Calloway als Anfang gelang es mir jedoch, etwas zum Laufen zu bringen. Hier ist die Grundfunktion.

def import_module_from_file(full_path_to_module):
    """
    Import a module given the full path/filename of the .py file

    Python 3.4

    """

    module = None

    try:

        # Get module name and path from full path
        module_dir, module_file = os.path.split(full_path_to_module)
        module_name, module_ext = os.path.splitext(module_file)

        # Get module "spec" from filename
        spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)

        module = spec.loader.load_module()

    except Exception as ec:
        # Simple error printing
        # Insert "sophisticated" stuff here
        print(ec)

    finally:
        return module

Dies scheint nicht veraltete Module aus Python 3.4 zu verwenden. Ich gebe nicht vor zu verstehen warum, aber es scheint innerhalb eines Programms zu funktionieren. Ich fand, dass Chris 'Lösung in der Kommandozeile funktionierte, aber nicht innerhalb eines Programms.

Redlegjed
quelle
4

Ich sage nicht, dass es besser ist, aber der Vollständigkeit halber wollte ich die execFunktion vorschlagen , die sowohl in Python 2 als auch in Python 3 verfügbar execist. Sie können beliebigen Code entweder im globalen Bereich oder in einem internen Bereich ausführen. als Wörterbuch zur Verfügung gestellt.

Wenn Sie beispielsweise ein Modul in "/path/to/module"mit der Funktion gespeichert haben foo(), können Sie es folgendermaßen ausführen:

module = dict()
with open("/path/to/module") as f:
    exec(f.read(), module)
module['foo']()

Dies macht es etwas expliziter, dass Sie Code dynamisch laden, und gewährt Ihnen zusätzliche Leistung, z. B. die Möglichkeit, benutzerdefinierte integrierte Funktionen bereitzustellen.

Wenn Ihnen der Zugriff über Attribute anstelle von Schlüsseln wichtig ist, können Sie eine benutzerdefinierte Diktatklasse für die Globals entwerfen, die einen solchen Zugriff bietet, z.

class MyModuleClass(dict):
    def __getattr__(self, name):
        return self.__getitem__(name)
yoniLavi
quelle
4

Um ein Modul aus einem bestimmten Dateinamen zu importieren, können Sie den Pfad vorübergehend erweitern und den Systempfad in der Referenz für den endgültigen Block wiederherstellen :

filename = "directory/module.py"

directory, module_name = os.path.split(filename)
module_name = os.path.splitext(module_name)[0]

path = list(sys.path)
sys.path.insert(0, directory)
try:
    module = __import__(module_name)
finally:
    sys.path[:] = path # restore
Peter Zhu
quelle
3

Das sollte funktionieren

path = os.path.join('./path/to/folder/with/py/files', '*.py')
for infile in glob.glob(path):
    basename = os.path.basename(infile)
    basename_without_extension = basename[:-3]

    # http://docs.python.org/library/imp.html?highlight=imp#module-imp
    imp.load_source(basename_without_extension, infile)
Hengjie
quelle
4
Eine allgemeinere Methode zum Ausschneiden der Verlängerung ist : name, ext = os.path.splitext(os.path.basename(infile)). Ihre Methode funktioniert aufgrund der vorherigen Einschränkung auf die Erweiterung .py. Außerdem sollten Sie das Modul wahrscheinlich in einen Variablen- / Wörterbucheintrag importieren.
ReneSac
3

Wenn wir Skripte im selben Projekt haben, aber in verschiedenen Verzeichnissen, können wir dieses Problem mit der folgenden Methode lösen.

In dieser Situation utils.pyist insrc/main/util/

import sys
sys.path.append('./')

import src.main.util.utils
#or
from src.main.util.utils import json_converter # json_converter is example method
Kumar Immanuel
quelle
2

Ich habe ein Paket erstellt, das impfür Sie verwendet wird. Ich nenne es import_fileund so wird es verwendet:

>>>from import_file import import_file
>>>mylib = import_file('c:\\mylib.py')
>>>another = import_file('relative_subdir/another.py')

Sie können es bekommen bei:

http://pypi.python.org/pypi/import_file

oder bei

http://code.google.com/p/import-file/

ubershmekel
quelle
1
os.chdir? (minimale Zeichen, um einen Kommentar zu genehmigen).
Ychaouche
Ich habe den ganzen Tag damit verbracht, einen Importfehler in einer von Pyinstaller generierten Exe zu beheben. Am Ende ist dies das einzige, was für mich funktioniert hat. Vielen Dank dafür!
Frakman1
2

Paketmodule zur Laufzeit importieren (Python-Rezept)

http://code.activestate.com/recipes/223972/

###################
##                #
## classloader.py #
##                #
###################

import sys, types

def _get_mod(modulePath):
    try:
        aMod = sys.modules[modulePath]
        if not isinstance(aMod, types.ModuleType):
            raise KeyError
    except KeyError:
        # The last [''] is very important!
        aMod = __import__(modulePath, globals(), locals(), [''])
        sys.modules[modulePath] = aMod
    return aMod

def _get_func(fullFuncName):
    """Retrieve a function object from a full dotted-package name."""

    # Parse out the path, module, and function
    lastDot = fullFuncName.rfind(u".")
    funcName = fullFuncName[lastDot + 1:]
    modPath = fullFuncName[:lastDot]

    aMod = _get_mod(modPath)
    aFunc = getattr(aMod, funcName)

    # Assert that the function is a *callable* attribute.
    assert callable(aFunc), u"%s is not callable." % fullFuncName

    # Return a reference to the function itself,
    # not the results of the function.
    return aFunc

def _get_class(fullClassName, parentClass=None):
    """Load a module and retrieve a class (NOT an instance).

    If the parentClass is supplied, className must be of parentClass
    or a subclass of parentClass (or None is returned).
    """
    aClass = _get_func(fullClassName)

    # Assert that the class is a subclass of parentClass.
    if parentClass is not None:
        if not issubclass(aClass, parentClass):
            raise TypeError(u"%s is not a subclass of %s" %
                            (fullClassName, parentClass))

    # Return a reference to the class itself, not an instantiated object.
    return aClass


######################
##       Usage      ##
######################

class StorageManager: pass
class StorageManagerMySQL(StorageManager): pass

def storage_object(aFullClassName, allOptions={}):
    aStoreClass = _get_class(aFullClassName, StorageManager)
    return aStoreClass(allOptions)
user10370
quelle
2

Unter Linux funktioniert das Hinzufügen eines symbolischen Links in dem Verzeichnis, in dem sich Ihr Python-Skript befindet.

dh:

ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py

Python erstellt /absolute/path/to/script/module.pycund aktualisiert es, wenn Sie den Inhalt von ändern/absolute/path/to/module/module.py

Fügen Sie dann Folgendes in mypythonscript.py ein

from module import *
user2760152
quelle
1
Dies ist der Hack, den ich verwendet habe, und er hat mir einige Probleme verursacht. Eine der schmerzhafteren war, dass IDEA ein Problem hat, bei dem geänderter Code nicht aus dem Link übernommen wird, aber dennoch versucht wird, das zu speichern, was seiner Meinung nach vorhanden ist. Eine Rennbedingung, bei der das Letzte, was gerettet werden muss, bleibt ... Ich habe dadurch eine anständige Menge an Arbeit verloren.
Gripp
@Gripp ist sich nicht sicher, ob ich Ihr Problem verstehe, aber ich bearbeite meine Skripte häufig (fast ausschließlich) auf einem Remote-Server von meinem Desktop über SFTP mit einem Client wie CyberDuck. Auch in diesem Fall ist es eine schlechte Idee, dies zu versuchen Bearbeiten Sie die verknüpfte Datei. Stattdessen ist es viel sicherer, die Originaldatei zu bearbeiten. Sie können einige dieser Probleme beheben, gitindem Sie Ihre verwenden und überprüfen, git statusum sicherzustellen, dass Ihre Änderungen am Skript tatsächlich zum Quelldokument zurückkehren und nicht im Äther verloren gehen.
user5359531
2

Ich habe meine eigene globale und tragbare Importfunktion basierend auf dem importlibModul geschrieben für:

  • Sie können beide Module als Submodul importieren und Inhalte eines Moduls in ein übergeordnetes Modul (oder in ein globales Modul, wenn kein übergeordnetes Modul vorhanden ist) importieren.
  • Sie können Module mit einem Punkt in einem Dateinamen importieren.
  • Sie können Module mit einer beliebigen Erweiterung importieren.
  • Sie können einen eigenständigen Namen für ein Submodul anstelle eines Dateinamens ohne Erweiterung verwenden, der standardmäßig verwendet wird.
  • Sie können die Importreihenfolge basierend auf dem zuvor importierten Modul definieren, anstatt von sys.pathoder von einem beliebigen Suchpfadspeicher abhängig zu sein .

Die Beispiele Verzeichnisstruktur:

<root>
 |
 +- test.py
 |
 +- testlib.py
 |
 +- /std1
 |   |
 |   +- testlib.std1.py
 |
 +- /std2
 |   |
 |   +- testlib.std2.py
 |
 +- /std3
     |
     +- testlib.std3.py

Einschlussabhängigkeit und Reihenfolge:

test.py
  -> testlib.py
    -> testlib.std1.py
      -> testlib.std2.py
    -> testlib.std3.py 

Implementierung:

Speicher für neueste Änderungen: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py

test.py :

import os, sys, inspect, copy

SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("test::SOURCE_FILE: ", SOURCE_FILE)

# portable import to the global space
sys.path.append(TACKLELIB_ROOT) # TACKLELIB_ROOT - path to the library directory
import tacklelib as tkl

tkl.tkl_init(tkl)

# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()

tkl_import_module(SOURCE_DIR, 'testlib.py')

print(globals().keys())

testlib.base_test()
testlib.testlib_std1.std1_test()
testlib.testlib_std1.testlib_std2.std2_test()
#testlib.testlib.std3.std3_test()                             # does not reachable directly ...
getattr(globals()['testlib'], 'testlib.std3').std3_test()     # ... but reachable through the `globals` + `getattr`

tkl_import_module(SOURCE_DIR, 'testlib.py', '.')

print(globals().keys())

base_test()
testlib_std1.std1_test()
testlib_std1.testlib_std2.std2_test()
#testlib.std3.std3_test()                                     # does not reachable directly ...
globals()['testlib.std3'].std3_test()                         # ... but reachable through the `globals` + `getattr`

testlib.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("1 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std1', 'testlib.std1.py', 'testlib_std1')

# SOURCE_DIR is restored here
print("2 testlib::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/std3', 'testlib.std3.py')

print("3 testlib::SOURCE_FILE: ", SOURCE_FILE)

def base_test():
  print('base_test')

testlib.std1.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std1::SOURCE_FILE: ", SOURCE_FILE)

tkl_import_module(SOURCE_DIR + '/../std2', 'testlib.std2.py', 'testlib_std2')

def std1_test():
  print('std1_test')

testlib.std2.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std2::SOURCE_FILE: ", SOURCE_FILE)

def std2_test():
  print('std2_test')

testlib.std3.py :

# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)

print("testlib.std3::SOURCE_FILE: ", SOURCE_FILE)

def std3_test():
  print('std3_test')

Ausgabe ( 3.7.4):

test::SOURCE_FILE:  <root>/test01/test.py
import : <root>/test01/testlib.py as testlib -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib'])
base_test
std1_test
std2_test
std3_test
import : <root>/test01/testlib.py as . -> []
1 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE:  <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE:  <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE:  <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE:  <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib', 'testlib_std1', 'testlib.std3', 'base_test'])
base_test
std1_test
std2_test
std3_test

Getestet in Python 3.7.4, 3.2.5,2.7.16

Vorteile :

  • Kann beide Module als Submodul importieren und Inhalte eines Moduls in ein übergeordnetes Modul (oder in ein globales Modul, wenn kein übergeordnetes Modul vorhanden ist) importieren.
  • Kann Module mit Punkten in einem Dateinamen importieren.
  • Kann jedes Erweiterungsmodul von jedem Erweiterungsmodul importieren.
  • Kann einen eigenständigen Namen für ein Submodul anstelle eines Dateinamens ohne Erweiterung verwenden, der standardmäßig verwendet wird (z. B. testlib.std.pyas testlib, testlib.blabla.pyas testlib_blablausw.).
  • Hängt nicht von einem sys.pathoder einem beliebigen Suchpfadspeicher ab.
  • Erfordert nicht das Speichern / Wiederherstellen globaler Variablen wie SOURCE_FILEund SOURCE_DIRzwischen Aufrufen von tkl_import_module.
  • [für 3.4.xund höher] Kann die Modul-Namespaces in verschachtelten tkl_import_moduleAufrufen mischen (z. B. named->local->namedoder local->named->localso weiter).
  • [für 3.4.xund höher] Kann globale Variablen / Funktionen / Klassen automatisch exportieren, von wo aus sie zu allen untergeordneten Modulen deklariert werden, die über tkl_import_module(über die tkl_declare_globalFunktion) importiert wurden .

Nachteile :

  • [für 3.3.xund niedriger] Erforderlich, tkl_import_modulein allen Modulen zu deklarieren, die aufrufen tkl_import_module(Codeduplizierung)

Update 1,2 (nur für 3.4.xund höher):

In Python 3.4 und höher können Sie die Anforderung umgehen, tkl_import_modulein jedem Modul zu deklarieren, indem Sie tkl_import_modulein einem Modul der obersten Ebene deklarieren, und die Funktion würde sich in einem einzigen Aufruf auf alle untergeordneten Module auswirken (dies ist eine Art Selbstbereitstellungsimport).

Update 3 :

Funktion tkl_source_moduleals analog zu Bash sourcemit Support Execution Guard beim Import hinzugefügt (implementiert durch die Modulzusammenführung anstelle des Imports).

Update 4 :

Funktion tkl_declare_globalzum automatischen Exportieren einer globalen Modulvariablen in alle untergeordneten Module hinzugefügt, in denen eine globale Modulvariable nicht sichtbar ist, da sie nicht Teil eines untergeordneten Moduls ist.

Update 5 :

Alle Funktionen wurden in die Tacklelib-Bibliothek verschoben, siehe den obigen Link.

Andry
quelle
2

Es gibt ein spezielles Paket :

from thesmuggler import smuggle

# À la `import weapons`
weapons = smuggle('weapons.py')

# À la `from contraband import drugs, alcohol`
drugs, alcohol = smuggle('drugs', 'alcohol', source='contraband.py')

# À la `from contraband import drugs as dope, alcohol as booze`
dope, booze = smuggle('drugs', 'alcohol', source='contraband.py')

Es wurde in allen Python-Versionen getestet (auch Jython und PyPy), kann jedoch je nach Größe Ihres Projekts zu viel des Guten sein.

fny
quelle
1

Hinzufügen dieser zur Liste der Antworten, da ich nichts finden konnte, was funktionierte. Dies ermöglicht den Import kompilierter (pyd) Python-Module in 3.4:

import sys
import importlib.machinery

def load_module(name, filename):
    # If the Loader finds the module name in this list it will use
    # module_name.__file__ instead so we need to delete it here
    if name in sys.modules:
        del sys.modules[name]
    loader = importlib.machinery.ExtensionFileLoader(name, filename)
    module = loader.load_module()
    locals()[name] = module
    globals()[name] = module

load_module('something', r'C:\Path\To\something.pyd')
something.do_something()
David
quelle
1

Ganz einfache Möglichkeit: Angenommen, Sie möchten eine Datei mit dem relativen Pfad importieren. / .. / MyLibs / pyfunc.py


libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf

Aber wenn Sie es ohne Wache schaffen, können Sie endlich einen sehr langen Weg zurücklegen

Andrei Keino
quelle
1

Eine einfache Lösung importlibanstelle des impPakets (getestet für Python 2.7, obwohl es auch für Python 3 funktionieren sollte):

import importlib

dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
sys.path.append(dirname) # only directories should be added to PYTHONPATH
module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")

Jetzt können Sie den Namespace des importierten Moduls direkt wie folgt verwenden:

a = module.myvar
b = module.myfunc(a)

Der Vorteil dieser Lösung ist, dass wir nicht einmal den tatsächlichen Namen des Moduls kennen müssen, das wir importieren möchten , um es in unserem Code zu verwenden. Dies ist nützlich, z. B. wenn der Pfad des Moduls ein konfigurierbares Argument ist.

Ataxien
quelle
Auf diese Weise ändern Sie das sys.path, was nicht für jeden Anwendungsfall geeignet ist.
Bgusach
@bgusach Dies mag zutreffen, ist aber in einigen Fällen auch wünschenswert (das Hinzufügen eines Pfads zu sys.path vereinfacht das Importieren von mehr als einem Modul aus einem einzelnen Paket). Auf jeden Fall, wenn dies nicht wünschenswert ist, kann man sofort danach tunsys.path.pop()
Ataxias
0

Diese Antwort ist eine Ergänzung zu Sebastian Rittaus Antwort auf den Kommentar: "Aber was ist, wenn Sie den Modulnamen nicht haben?" Dies ist eine schnelle und schmutzige Methode, um den wahrscheinlichen Namen des Python-Moduls mit einem Dateinamen zu versehen. Er geht nur den Baum hoch, bis ein Verzeichnis ohne __init__.pyDatei gefunden wird, und wandelt es dann wieder in einen Dateinamen um. Für Python 3.4+ (verwendet pathlib) ist dies sinnvoll, da Py2-Benutzer "imp" oder andere Methoden für relative Importe verwenden können:

import pathlib

def likely_python_module(filename):
    '''
    Given a filename or Path, return the "likely" python module name.  That is, iterate
    the parent directories until it doesn't contain an __init__.py file.

    :rtype: str
    '''
    p = pathlib.Path(filename).resolve()
    paths = []
    if p.name != '__init__.py':
        paths.append(p.stem)
    while True:
        p = p.parent
        if not p:
            break
        if not p.is_dir():
            break

        inits = [f for f in p.iterdir() if f.name == '__init__.py']
        if not inits:
            break

        paths.append(p.stem)

    return '.'.join(reversed(paths))

Es gibt sicherlich Verbesserungsmöglichkeiten, und die optionalen __init__.pyDateien erfordern möglicherweise andere Änderungen, aber wenn Sie __init__.pyim Allgemeinen haben, ist dies der Trick.

Michael Scott Cuthbert
quelle
-1

Ich denke, der beste Weg ist aus der offiziellen Dokumentation ( 29.1. Imp - Zugriff auf die Import-Interna ):

import imp
import sys

def __import__(name, globals=None, locals=None, fromlist=None):
    # Fast path: see if the module has already been imported.
    try:
        return sys.modules[name]
    except KeyError:
        pass

    # If any of the following calls raises an exception,
    # there's a problem we can't handle -- let the caller handle it.

    fp, pathname, description = imp.find_module(name)

    try:
        return imp.load_module(name, fp, pathname, description)
    finally:
        # Since we may exit via an exception, close fp explicitly.
        if fp:
            fp.close()
Zompa
quelle
1
Mit dieser Lösung können Sie den Pfad nicht angeben, nach dem in der Frage gefragt wird.
Micah Smith