2010-06-29 10 views
8

Dieses Snippet stammt von einer früheren Antwort hier auf SO. Es ist ungefähr ein Jahr alt (und die Antwort wurde nicht akzeptiert). Ich bin neu in Python und ich finde den Systempfad einen echten Schmerz. Ich habe ein paar Funktionen in Skripten in verschiedenen Verzeichnissen geschrieben, und ich würde gerne in der Lage sein, sie in neue Projekte zu importieren, ohne durch die Ringe springen zu müssen.Ist dies der richtige Weg, um Python-Skripte in beliebigen Ordnern zu importieren?

Dies ist der Code-Schnipsel:

def import_path(fullpath): 
""" Import a file with full path specification. Allows one to 
    import from anywhere, something __import__ does not do. 
""" 
path, filename = os.path.split(fullpath) 
filename, ext = os.path.splitext(filename) 
sys.path.append(path) 
module = __import__(filename) 
reload(module) # Might be out of date 
del sys.path[-1] 
return module 

Sein von hier: How to do relative imports in Python?

ich ein Feedback, ob ich es oder nicht verwenden kann, möchte - und wenn es irgendwelche unerwünschten Nebenwirkungen, die ist für einen Neuling vielleicht nicht offensichtlich.

Ich beabsichtige es so etwas zu verwenden:

import_path(/home/pydev/path1/script1.py) 

script1.func1() 

etc

Ist es ‚sicher‘ die Funktion in der Art und Weise zu verwenden, ich beabsichtige?

Antwort

8

Der "offizielle" und völlig sichere Ansatz ist das imp-Modul der Standard-Python-Bibliothek.

Verwenden imp.find_module das Modul auf genau festgelegte Liste akzeptabler Verzeichnisse zu finden - es gibt eine 3-Tupel (file, pathname, description) - wenn nicht erfolgreich, file tatsächlich None ist (es kann aber auch ImportError erhöhen, so sollten Sie ein try verwenden/except dafür sowie die Überprüfung if file is None:).

Ist die Suche erfolgreich ist, rufen imp.load_module (in einem try/finally um sicherzustellen, dass Sie die Datei schließen!) Mit den drei oben genannten Argumente nach der ersten, die die gleiche name Sie find_module geben werden müssen - es gibt das Modulobjekt (Puh ;-).

+0

In 3.3 wurde imp zugunsten von importlib eingestellt. In 3.2 scheint es jedoch keinen gleichwertigen Gebrauch von importlib zu geben. Wenn Sie also in beiden Umgebungen laufen müssen, sparen Sie sich Kopfschmerzen und bleiben Sie bei Imp. – Yourpalal

0

Es fühlt sich an wie ein bisschen ein Hack, aber im Moment kann ich nicht an unbeabsichtigte Nebenwirkungen denken, die wahrscheinlich auftreten werden, zumindest nicht so lange, wie Sie dies nur für Ihre eigenen verwenden Skripte. Im Grunde wird das übergeordnete Verzeichnis der angegebenen Datei (in Ihrem Beispiel /home/pydev/path1/) der Liste der Pfade hinzugefügt, die Python bei der Suche nach einem zu importierenden Modul überprüft.

Das einzige Risiko, das ich mir gerade vorstellen kann, würde in einer Multithread-Umgebung entstehen, wo zwei oder mehr Threads (oder Prozesse) diese Funktion gleichzeitig ausführen. Wenn Thread A Modul A aus dem Pfad dirA/A.py importieren möchte und Thread B Modul B aus dem Pfad dirB/B.py importieren möchte, werden Sie für eine kurze Zeit mit dirA und dirB in sys.path fertig. Und wenn es eine Datei mit dem Namen B.py in dirA gibt, ist es möglich, dass Thread B (dirA/B.py) statt der Datei, nach der er sucht (dirB/B.py), das falsche Modul importiert. Aus diesem Grund würde ich es nicht in Produktionscode oder Code verwenden, den Sie an andere Leute verteilen werden (zumindest nicht, ohne sie zu warnen, dass dieser Hack hier ist!). In einer solchen Situation könnten Sie eine komplexere Funktion schreiben, mit der Sie die zu importierende Datei angeben können, ohne die Standardpfade zu stören. (Das macht beispielsweise mod_python)

0

Ich hätte Sorge, dass Ihr Skriptname mit einem Modul übereinstimmen könnte, das früher im Pfad angezeigt wird. Um diese Angst zu zerstreuen, würde ich den Pfad vollständig durch eine neue Liste ersetzen, die nur das Verzeichnis enthält, das das Modul enthält, und es nach dem Import zurückstellen. Auch, sollten Sie dies in eine Art von Sperre Wrap so, dass mehrere Threads, die versuchen, die gleiche Sache nicht miteinander stören.

0

Wie erwähnt, beachten Sie ggf. die Fadensicherheit. Ich bevorzuge etwas, das näher an einer Lösung ist, die in einem ähnlichen Beitrag gepostet wird. Die Hauptunterschiede unten: die Verwendung von Einfügung, um die Priorität des Imports zu spezifizieren, die korrekte Wiederherstellung von sys.path mit try ... schließlich und das Setzen des globalen Namespace.

# inspired by Alex Martelli's solution to 
# http://stackoverflow.com/questions/1096216/override-namespace-in-python/1096247#1096247 
def import_from_absolute_path(fullpath, global_name=None): 
    """Dynamic script import using full path.""" 
    import os 
    import sys 

    script_dir, filename = os.path.split(fullpath) 
    script, ext = os.path.splitext(filename) 

    sys.path.insert(0, script_dir) 
    try: 
     module = __import__(script) 
     if global_name is None: 
      global_name = script 
     globals()[global_name] = module 
     sys.modules[global_name] = module 
    finally: 
     del sys.path[0] 
Verwandte Themen