Wie erhalte ich den Pfad der aktuell ausgeführten Datei in Python?

193

Dies mag wie eine Anfängerfrage erscheinen, ist es aber nicht. Einige gängige Ansätze funktionieren nicht in allen Fällen:

sys.argv [0]

Dies bedeutet, dass Sie verwenden path = os.path.abspath(os.path.dirname(sys.argv[0])), dies funktioniert jedoch nicht, wenn Sie ein anderes Python-Skript in einem anderen Verzeichnis ausführen. Dies kann im wirklichen Leben passieren.

__Datei__

Dies bedeutet verwenden path = os.path.abspath(os.path.dirname(__file__)), aber ich fand, dass dies nicht funktioniert:

  • py2exehat kein __file__Attribut, aber es gibt eine Problemumgehung
  • Wenn Sie von IDLE mit ausführen, execute()gibt es kein __file__Attribut
  • OS X 10.6 wo ich hinkomme NameError: global name '__file__' is not defined

Verwandte Fragen mit unvollständigen Antworten:

Ich suche nach einer generischen Lösung , die in allen oben genannten Anwendungsfällen funktioniert.

Aktualisieren

Hier ist das Ergebnis eines Testfalls:

Ausgabe von Python a.py (unter Windows)

a.py: __file__= a.py
a.py: os.getcwd()= C:\zzz

b.py: sys.argv[0]= a.py
b.py: __file__= a.py
b.py: os.getcwd()= C:\zzz

a.py.

#! /usr/bin/env python
import os, sys

print "a.py: sys.argv[0]=", sys.argv[0]
print "a.py: __file__=", __file__
print "a.py: os.getcwd()=", os.getcwd()
print

execfile("subdir/b.py")

subdir / b.py.

#! /usr/bin/env python
import os, sys

print "b.py: sys.argv[0]=", sys.argv[0]
print "b.py: __file__=", __file__
print "b.py: os.getcwd()=", os.getcwd()
print

Baum

C:.
|   a.py
\---subdir
        b.py
Sorin
quelle

Antworten:

80

Sie können den Speicherort des ausgeführten Hauptskripts nicht direkt bestimmen. Schließlich stammte das Skript manchmal überhaupt nicht aus einer Datei. Zum Beispiel könnte es vom interaktiven Interpreter oder dynamisch generiertem Code stammen, der nur im Speicher gespeichert ist.

Sie können jedoch den Speicherort eines Moduls zuverlässig bestimmen, da Module immer aus einer Datei geladen werden. Wenn Sie ein Modul mit dem folgenden Code erstellen und es im selben Verzeichnis wie Ihr Hauptskript ablegen, kann das Hauptskript das Modul importieren und es verwenden, um sich selbst zu suchen.

some_path / module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

some_path / main.py:

import module_locator
my_path = module_locator.module_path()

Wenn Sie mehrere Hauptskripte in verschiedenen Verzeichnissen haben, benötigen Sie möglicherweise mehr als eine Kopie von module_locator.

Wenn Ihr Hauptskript von einem anderen Tool geladen wird, mit dem Sie keine Module importieren können, die sich zusammen mit Ihrem Skript befinden, haben Sie natürlich kein Glück. In solchen Fällen sind die Informationen, nach denen Sie suchen, einfach nirgendwo in Ihrem Programm vorhanden. Am besten melden Sie einen Fehler bei den Autoren des Tools.

Daniel Stutzbach
quelle
2
Ich erwähne, dass ich NameError: global name '__file__' is not definedunter OS 10.6 eine Datei verwende und diese nicht im IDLE enthalten ist. Denken Sie, dass dies __file__nur innerhalb von Modulen definiert ist.
Sorin
1
@ Sorin Sbarnea: Ich habe meine Antwort dahingehend aktualisiert, wie ich das umgehen kann.
Daniel Stutzbach
2
Danke, aber tatsächlich hatte das Problem mit dem Fehlen __file__nichts mit Unicode zu tun. Ich weiß nicht, warum __file__nicht definiert ist, aber ich suche nach einer generischen Lösung, die in allen Fällen funktioniert.
Sorin
1
Dies ist leider nicht in allen Fällen möglich. Zum Beispiel versuche ich dies in waf.googlecode.com aus einer wscript-Datei (Python) heraus zu tun. Diese Dateien werden ausgeführt, sind jedoch keine Module, und Sie können sie nicht zu Modulen machen (sie können ein beliebiges Unterverzeichnis aus dem Quellbaum sein).
Sorin
1
Wird Ihnen das nicht some_path/module_locator.pystattdessen den Standort geben ?
Casebash
68

Zunächst müssen Sie aus inspectund importierenos

from inspect import getsourcefile
from os.path import abspath

Als nächstes verwenden Sie einfach, wo immer Sie die Quelldatei von Ihnen finden möchten

abspath(getsourcefile(lambda:0))
ArtOfWarfare
quelle
Beste Antwort. Danke dir.
Devan Williams
4
Gute Antwort. Vielen Dank. Es scheint auch die kürzeste und portabelste Antwort zu sein (läuft auf verschiedenen Betriebssystemen) und es treten keine Probleme auf wie NameError: global name '__file__' is not defined(eine andere Lösung hat dies verursacht).
Edward
Eine andere Möglichkeit, die ebenso kurz ist : lambda:_. Es hat bei mir funktioniert - ich bin mir nicht sicher, ob es immer funktionieren wird. lambda:0läuft wahrscheinlich um einen unermesslich kleinen Betrag schneller (oder vielleicht nicht so klein ... könnte eine sofortige Ladung sein oder etwas noch schnelleres als 0eine globale Last für _?). Es ist fraglich, ob es sauberer oder leichter zu lesen oder noch klüger / undurchsichtiger ist.
ArtOfWarfare
Wie bei fast jedem Vorschlag, den ich versucht habe, gibt dies nur den cwd für mich zurück, nicht die Verzeichnisdatei, die ich ausführe (Debugging).
James
1
@James - Dieser Code ist in der Datei enthalten, die Sie ausführen. Das Ausführen getsourcefile(lambda:0)ist bedeutungslos und wird nur zurückgegeben, Nonewenn Sie versuchen, ihn an einer interaktiven Eingabeaufforderung auszuführen (da er lambdain keiner Quelldatei enthalten ist.) Wenn Sie dies wissen möchten Woher kommt eine andere Funktion oder ein anderes Objekt in einer interaktiven Umgebung abspath(getsourcefile(thatFunctionOrObject))? Vielleicht ist es für Sie hilfreicher?
ArtOfWarfare
16

Diese Lösung ist auch in ausführbaren Dateien robust

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))
José Crespo Barrios
quelle
2
Dies sollte die richtige Antwort sein. Dies funktioniert auch in entry_point: console_script, aber keine der anderen Antworten.
Polv
15

Ich hatte ein ähnliches Problem und denke, dies könnte das Problem lösen:

def module_path(local_function):
   ''' returns the module path without the use of __file__.  Requires a function defined
   locally in the module.
   from http://stackoverflow.com/questions/729583/getting-file-path-of-imported-module'''
   return os.path.abspath(inspect.getsourcefile(local_function))

Es funktioniert für normale Skripte und im Leerlauf. Ich kann nur sagen, probieren Sie es für andere aus!

Meine typische Verwendung:

from toolbox import module_path
def main():
   pass # Do stuff

global __modpath__
__modpath__ = module_path(main)

Jetzt benutze ich __modpath__ anstelle von __file__.

Garrett Berg
quelle
2
Gemäß dem PEP8- Codierungsstil-Guide sollte man niemals Namen mit doppelt führenden und nachfolgenden Unterstrichen erstellen - __modpath__sollte also umbenannt werden. Sie brauchen die globalAussage wahrscheinlich auch nicht . Sonst +1!
Martineau
4
Tatsächlich können Sie die lokale Funktion direkt im Aufruf von definieren module_path(). dh module_path(lambda _: None)was nicht von den anderen Inhalten des Skripts abhängt, in dem es sich befindet.
Martineau
@martineau: Ich habe Ihren Vorschlag angenommen lambda _: Noneund ihn fast die letzten zwei Jahre verwendet, aber gerade jetzt habe ich festgestellt, dass ich ihn auf nur reduzieren kann lambda:0. Gibt es einen bestimmten Grund, warum Sie das Formular vorgeschlagen haben, mit einem ignorierten Argument von _, anstatt überhaupt kein Argument? Gibt es etwas Überlegenes daran, Nonewenn ein Leerzeichen vorangestellt wird und nicht nur 0? Sie sind beide gleich kryptisch, ich denke, nur einer ist 8 Zeichen lang, während der andere 14 Zeichen lang ist.
ArtOfWarfare
@ArtOfWarfare: Der Unterschied zwischen den beiden besteht darin, dass lambda _:es sich um eine Funktion handelt, die ein Argument akzeptiert und lambda:keine. Es spielt keine Rolle, da die Funktion nie aufgerufen wird. Ebenso spielt es keine Rolle, welcher Rückgabewert verwendet wird. Ich denke, ich habe mich entschieden, Noneweil es zu der Zeit darauf hindeutete, dass es sich um eine Funktion handelte, die nichts zu tun hatte und nie besser genannt werden sollte. Der Platz davor ist optional und wiederum nur zur besseren Lesbarkeit vorhanden (immer der Versuch, PEP8 zu folgen, ist gewohnheitsbildend).
Martineau
@martineau: Es ist offensichtlich ein Missbrauch lambda, es für etwas zu verwenden, für das es nie gedacht war. Wenn Sie PEP8 folgen sollten, denke ich , die richtigen Inhalte für sich wären pass, nicht None, aber es ist nicht gültig , eine Erklärung in einem setzen lambda, so dass Sie in etwas mit einem Wert zu setzen haben. Es gibt ein paar gültige 2-Zeichen-Dinge, die Sie eingeben könnten, aber ich denke, die einzigen gültigen Einzelzeichen-Dinge, die Sie eingeben können, sind 0-9 (oder ein einzelner Zeichenvariablenname, der außerhalb von zugewiesen ist lambda). Ich denke, am 0besten zeigt das Nichts von 0- an. 9.
ArtOfWarfare
6

Die kurze Antwort lautet: Es gibt keine garantierte Möglichkeit, die gewünschten Informationen zu erhalten. Es gibt jedoch Heuristiken, die in der Praxis fast immer funktionieren. Sie könnten sich ansehen, wie ich den Speicherort der ausführbaren Datei in C finde. . Es wird das Problem aus C-Sicht erörtert, aber die vorgeschlagenen Lösungen lassen sich leicht in Python übertragen.

Dale Hagglund
quelle
Hallo, meine Flagge wurde abgelehnt, daher habe ich jetzt eine Diskussion über Meta gestartet
ArtOfWarfare
Es gibt jedoch bereits einfache Arbeitsantworten auf dieser Seite. Meine Lösung funktioniert gut: stackoverflow.com/a/33531619/3787376 .
Edward
5

In meiner Antwort auf die Frage Importieren von Modulen aus dem übergeordneten Ordner finden Sie verwandte Informationen, einschließlich der Frage , warum meine Antwort die unzuverlässige __file__Variable nicht verwendet . Diese einfache Lösung sollte mit verschiedenen Betriebssystemen wie den Modulen osund kompatibel seininspect Teil von Python sein.

Zunächst müssen Sie Teile der Inspect- und OS- Module importieren .

from inspect import getsourcefile
from os.path import abspath

Verwenden Sie als Nächstes die folgende Zeile an einer anderen Stelle in Ihrem Python-Code:

abspath(getsourcefile(lambda:0))

Wie es funktioniert:

Aus dem eingebauten Modul os(Beschreibung unten) wird das abspathTool importiert.

Betriebssystemroutinen für Mac, NT oder Posix, je nachdem auf welchem ​​System wir uns befinden.

Dann wird getsourcefile(Beschreibung unten) aus dem eingebauten Modul importiert inspect.

Erhalten Sie nützliche Informationen von Live-Python-Objekten.

  • abspath(path) Gibt die absolute / vollständige Version eines Dateipfads zurück
  • getsourcefile(lambda:0)Ruft irgendwie die interne Quelldatei des Lambda-Funktionsobjekts ab, kehrt also '<pyshell#nn>'in der Python-Shell zurück oder gibt den Dateipfad des aktuell ausgeführten Python-Codes zurück.

Wenn Sie abspathdas Ergebnis von verwenden, getsourcefile(lambda:0)sollten Sie sicherstellen, dass der generierte Dateipfad der vollständige Dateipfad der Python-Datei ist.
Diese erläuterte Lösung basierte ursprünglich auf Code aus der Antwort unter Wie erhalte ich den Pfad der aktuell ausgeführten Datei in Python? .

Edward
quelle
Ja, ich habe gerade die gleiche Lösung gefunden ... viel besser als die Antworten, die nur sagen, dass dies nicht zuverlässig möglich ist ... es sei denn, die Frage fragt nicht, was ich denke ...
Grady Player
5

Sie haben einfach angerufen:

path = os.path.abspath(os.path.dirname(sys.argv[0]))

anstatt:

path = os.path.dirname(os.path.abspath(sys.argv[0]))

abspath()gibt Ihnen den absoluten Pfad von sys.argv[0](den Dateinamen, in dem sich Ihr Code befindet) und dirname()gibt den Verzeichnispfad ohne den Dateinamen zurück.

dgb
quelle
3

Dies sollte den Trick plattformübergreifend ausführen (solange Sie nicht den Interpreter oder etwas anderes verwenden):

import os, sys
non_symbolic=os.path.realpath(sys.argv[0])
program_filepath=os.path.join(sys.path[0], os.path.basename(non_symbolic))

sys.path[0]ist das Verzeichnis, in dem sich Ihr aufrufendes Skript befindet (der erste Ort, an dem nach Modulen gesucht wird, die von diesem Skript verwendet werden sollen). Wir können den Namen der Datei selbst am Ende von sys.argv[0]entfernen (was ich damit gemacht habe os.path.basename). os.path.joinklebt sie einfach plattformübergreifend zusammen.os.path.realpathStellt nur sicher, dass wir immer noch den richtigen Namen des Skripts erhalten, wenn wir symbolische Links mit anderen Namen als dem Skript selbst erhalten.

Ich habe keinen Mac. Also habe ich das nicht an einem getestet. Bitte lassen Sie mich wissen, ob es funktioniert, wie es scheint. Ich habe dies unter Linux (Xubuntu) mit Python 3.4 getestet. Beachten Sie, dass viele Lösungen für dieses Problem auf Macs nicht funktionieren (da ich gehört habe, dass __file__dies auf Macs nicht vorhanden ist).

Beachten Sie, dass Ihr Skript, wenn es sich um eine symbolische Verknüpfung handelt, den Pfad der Datei angibt, mit der es verknüpft ist (und nicht den Pfad der symbolischen Verknüpfung).

Brōtsyorfuzthrāx
quelle
2

Sie können Pathaus dem pathlibModul verwenden:

from pathlib import Path

# ...

Path(__file__)

Sie können call verwenden, parentum den Pfad weiter zu verfolgen:

Path(__file__).parent
Gavriel Cohen
quelle
Diese Antwort verwendet die __file__Variable, die unzuverlässig sein kann (nicht immer der vollständige Dateipfad, funktioniert nicht auf jedem Betriebssystem usw.), wie StackOverflow-Benutzer häufig erwähnt haben. Wenn Sie die Antwort so ändern, dass sie nicht enthalten ist, treten weniger Probleme auf und sie ist besser kompatibel. Weitere Informationen finden Sie unter stackoverflow.com/a/33532002/3787376 .
Edward
@ mrroot5 Ok, also bitte deinen Kommentar löschen.
Gavriel Cohen
1

Wenn der Code aus einer Datei stammt, können Sie den vollständigen Namen abrufen

sys._getframe().f_code.co_filename

Sie können den Funktionsnamen auch als abrufen f_code.co_name

Alex Cohn
quelle
0

Fügen Sie einfach Folgendes hinzu:

from sys import *
path_to_current_file = sys.argv[0]
print(path_to_current_file)

Oder:

from sys import *
print(sys.argv[0])
H. Kamran
quelle
0

Meine Lösung ist:

import os
print(os.path.dirname(os.path.abspath(__file__)))
ThienSuBS
quelle
-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))
Mohamed.Abdo
quelle
Das macht keinen Sinn. Erstens '__file__'sollte es keine Zeichenfolge sein, zweitens __file__würde dies nur für die Datei funktionieren, in der sich diese Codezeile befindet, und nicht für die Datei, die ausgeführt wird?
Andreas Storvik Strauman