Jak uzyskać ścieżkę do aktualnie wykonywanego pliku w Pythonie?

193

To może wydawać się pytaniem dla początkujących, ale tak nie jest. Niektóre typowe podejścia nie działają we wszystkich przypadkach:

sys.argv [0]

Oznacza to używanie path = os.path.abspath(os.path.dirname(sys.argv[0])), ale to nie działa, jeśli uruchamiasz z innego skryptu Python w innym katalogu, co może się zdarzyć w prawdziwym życiu.

__plik__

Oznacza to używanie path = os.path.abspath(os.path.dirname(__file__)), ale stwierdziłem, że to nie działa:

  • py2exenie ma __file__atrybutu, ale istnieje obejście
  • Po uruchomieniu z IDLE execute()bez __file__atrybutu
  • OS X 10.6, gdzie dostaję NameError: global name '__file__' is not defined

Powiązane pytania z niepełnymi odpowiedziami:

Szukam ogólnego rozwiązania , które działałoby we wszystkich powyższych przypadkach użycia.

Aktualizacja

Oto wynik przypadku testowego:

Dane wyjściowe Python a.py (w systemie 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

drzewo

C:.
|   a.py
\---subdir
        b.py
sorin
źródło

Odpowiedzi:

80

Nie można bezpośrednio określić lokalizacji wykonywanego skryptu głównego. W końcu czasami skrypt wcale nie pochodzi z pliku. Na przykład może pochodzić z interaktywnego interpretera lub dynamicznie generowanego kodu przechowywanego tylko w pamięci.

Można jednak niezawodnie określić lokalizację modułu, ponieważ moduły są zawsze ładowane z pliku. Jeśli utworzysz moduł z następującym kodem i umieścisz go w tym samym katalogu, co skrypt główny, skrypt główny może zaimportować moduł i użyć go do zlokalizowania się.

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()

Jeśli masz kilka głównych skryptów w różnych katalogach, możesz potrzebować więcej niż jednej kopii modułu_lokator.

Oczywiście, jeśli twój główny skrypt jest ładowany przez inne narzędzie, które nie pozwala importować modułów znajdujących się w tym samym skrypcie, to nie masz szczęścia. W takich przypadkach poszukiwane informacje po prostu nie istnieją nigdzie w twoim programie. Najlepszym rozwiązaniem byłoby zgłoszenie błędu autorom narzędzia.

Daniel Stutzbach
źródło
2
Wspominam, że w OS 10.6 NameError: global name '__file__' is not definedużywam pliku i nie ma go w IDLE. Pomyśl, że __file__jest zdefiniowane tylko w modułach.
sorin
1
@Sorin Sbarnea: Zaktualizowałem swoją odpowiedź, jak sobie z tym poradzić.
Daniel Stutzbach,
2
Dzięki, ale w rzeczywistości problem z brakowaniem __file__nie miał nic wspólnego z Unicode. Nie wiem, dlaczego __file__nie został zdefiniowany, ale szukam ogólnego rozwiązania, które zadziała we wszystkich przypadkach.
sorin
1
Niestety nie jest to możliwe we wszystkich przypadkach. Na przykład próbuję to zrobić w waf.googlecode.com z pliku wscript (python). Pliki te są wykonywane, ale nie są modułami i nie można ich tworzyć (mogą to być dowolne podkatalogi z drzewa źródłowego).
sorin
1
Czy zamiast tego nie podasz lokalizacji some_path/module_locator.py?
Casebash,
68

Najpierw musisz zaimportować z inspectios

from inspect import getsourcefile
from os.path import abspath

Następnie, gdziekolwiek chcesz znaleźć plik źródłowy, po prostu użyj

abspath(getsourcefile(lambda:0))
ArtOfWarfare
źródło
Najlepsza odpowiedź. Dziękuję Ci.
Devan Williams
4
Świetna odpowiedź. Dzięki. Wydaje się również, że jest to najkrótsza i najbardziej przenośna odpowiedź (działająca na różnych systemach operacyjnych) i nie napotyka problemów takich jak NameError: global name '__file__' is not defined(spowodowało to inne rozwiązanie).
Edward,
Inną możliwością, która jest równie krótki: lambda:_. To działało dla mnie - nie jestem pewien, czy zawsze będzie działać. lambda:0prawdopodobnie działa szybciej o niezmiernie małą ilość (a może nie jest tak mały ... może być natychmiastowym obciążeniem lub czymś jeszcze szybszym w 0porównaniu do obciążenia globalnego dla _?) Dyskusyjne, czy jest to czystsze, łatwiejsze do odczytania, czy nawet bardziej sprytne / niejasne.
ArtOfWarfare
Jak w przypadku prawie każdej propozycji, którą wypróbowałem, to po prostu zwraca mi plik cwd, a nie plik katalogu, który uruchamiam (debugowanie).
James
1
@James - ten kod znajduje się w uruchomionym pliku ... uruchomiony getsourcefile(lambda:0)będzie bez znaczenia i po prostu wróci, Nonejeśli spróbujesz uruchomić go w interaktywnym monitie (ponieważ lambdanie będzie go w żadnym pliku źródłowym). Jeśli chcesz wiedzieć skąd pochodzi inna funkcja lub obiekt w interaktywnym środowisku, może abspath(getsourcefile(thatFunctionOrObject))będzie dla ciebie bardziej pomocny?
ArtOfWarfare
16

to rozwiązanie jest niezawodne nawet w plikach wykonywalnych

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))
José Crespo Barrios
źródło
2
To powinna być poprawna odpowiedź. Działa to nawet w entry_point: console_script, ale żadnej z pozostałych odpowiedzi.
Polv
15

Miałem podobny problem i myślę, że to może rozwiązać problem:

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))

Działa dla zwykłych skryptów i bezczynności. Mogę tylko wypróbować to dla innych!

Moje typowe zastosowanie:

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

global __modpath__
__modpath__ = module_path(main)

Teraz używam __modpath__ zamiast __file__.

Garrett Berg
źródło
2
Zgodnie z przewodnikiem po stylu kodowania PEP8 , nigdy nie należy tworzyć nazw z podwójnymi wiodącymi i końcowymi podkreślnikami - dlatego __modpath__należy zmienić nazwę. Prawdopodobnie też nie potrzebujesz tego globaloświadczenia. W przeciwnym razie +1!
martineau
4
W rzeczywistości możesz zdefiniować funkcję lokalną bezpośrednio w wywołaniu do module_path(). tzn. module_path(lambda _: None)która nie zależy od innej zawartości skryptu, w którym się znajduje.
martineau,
@martineau: Przyjąłem twoją sugestię lambda _: Nonei korzystałem z niej przez prawie ostatnie dwa lata, ale teraz odkryłem, że mogę ją skondensować do samego końca lambda:0. Czy jest jakiś konkretny powód, dla którego zasugerowałeś formularz, który zignorowałeś _zamiast argumentu? Czy jest coś lepszego w Noneprefiksie ze spacją niż tylko 0? Oba są równie tajemnicze, jak sądzę, tylko jedna ma 8 znaków, a druga ma 14 znaków.
ArtOfWarfare
@ArtOfWarfare: Różnica między nimi polega na tym, że lambda _:jest to funkcja, która przyjmuje jeden argument i lambda:nie przyjmuje żadnego. Nie ma to znaczenia, ponieważ funkcja nigdy nie jest wywoływana. Podobnie nie ma znaczenia, jaka wartość zwracana jest używana. Wydaje mi się, że wybrałem, Noneponieważ wtedy wydawało się, że jest to lepsza funkcja „nic nie rób, nigdy nie można nazywać”. Przestrzeń przed nim jest opcjonalna i znowu tam tylko dla lepszej czytelności (zawsze próba podążania za PEP8 kształtuje nawyki).
martineau
@martineau: Oczywiście jest to jednak nadużycie lambda, wykorzystywanie go do czegoś, czego nigdy nie zamierzano robić. Jeśli miałbyś podążać za PEP8, myślę, że odpowiednia treść byłaby pass, nie None, ale nie jest poprawne umieszczanie instrukcji w lambda, więc musisz wstawić coś o wartości. Jest kilka ważnych 2-znakowych rzeczy, które możesz umieścić, ale myślę, że jedyne poprawne pojedyncze znaki, które możesz wstawić, to 0-9 (lub nazwa zmiennej pojedynczego znaku przypisana poza lambda.). Moim zdaniem 0najlepiej wskazuje na nicość 0- 9
ArtOfWarfare
6

Krótka odpowiedź jest taka, że nie ma gwarantowanego sposobu na uzyskanie potrzebnych informacji , jednak istnieją heurystyki, które działają prawie zawsze w praktyce. Możesz spojrzeć na Jak znaleźć lokalizację pliku wykonywalnego w C? . Omawia problem z punktu widzenia C, ale proponowane rozwiązania można łatwo przepisać na język Python.

Dale Hagglund
źródło
Witam, moja flaga została odrzucona, więc rozpocząłem dyskusję na temat meta: meta.stackoverflow.com/questions/277272/...
ArtOfWarfare
Na tej stronie są jednak proste, działające odpowiedzi. Moje rozwiązanie działa dobrze: stackoverflow.com/a/33531619/3787376 .
Edward,
5

Zobacz moją odpowiedź na pytanie Importowanie modułów z folderu nadrzędnego w celu uzyskania powiązanych informacji, w tym dlaczego moja odpowiedź nie używa nierzetelnej __file__zmiennej. To proste rozwiązanie powinno być kompatybilne krzyżowo z różnymi systemami operacyjnymi jako modułami osi inspectbyć częścią Pythona.

Najpierw musisz zaimportować części modułów inspekcji i systemu operacyjnego .

from inspect import getsourcefile
from os.path import abspath

Następnie użyj następującego wiersza w dowolnym innym miejscu w kodzie Python:

abspath(getsourcefile(lambda:0))

Jak to działa:

Z wbudowanego modułu os(opis poniżej) abspathnarzędzie jest importowane.

Procedury systemu operacyjnego dla komputerów Mac, NT lub Posix w zależności od używanego systemu.

Następnie getsourcefile(opis poniżej) jest importowany z wbudowanego modułu inspect.

Uzyskaj przydatne informacje z aktywnych obiektów Python.

  • abspath(path) zwraca bezwzględną / pełną wersję ścieżki do pliku
  • getsourcefile(lambda:0)w jakiś sposób pobiera wewnętrzny plik źródłowy obiektu funkcji lambda, więc zwraca '<pyshell#nn>'w powłoce Pythona lub zwraca ścieżkę pliku aktualnie wykonywanego kodu Python.

Użycie abspathwyniku getsourcefile(lambda:0)powinno upewnić się, że wygenerowana ścieżka do pliku jest pełną ścieżką do pliku Python.
To wyjaśnione rozwiązanie zostało pierwotnie oparte na kodzie z odpowiedzi na pytanie Jak uzyskać ścieżkę do aktualnie wykonywanego pliku w Pythonie? .

Edward
źródło
tak, właśnie wymyśliłem to samo rozwiązanie ... o wiele lepsze niż odpowiedzi, które mówią, że nie można tego zrobić niezawodnie ... chyba że pytanie nie zadaje tego, co myślę ...
Grady Player,
5

Po prostu zadzwoniłeś:

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

zamiast:

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

abspath()podaje bezwzględną ścieżkę sys.argv[0](nazwa pliku, w którym znajduje się kod) i dirname()zwraca ścieżkę katalogu bez nazwy pliku.

dgb
źródło
4

Powinno to załatwić sprawę na wiele platform (o ile nie używasz interpretera lub czegoś takiego):

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]to katalog, w którym znajduje się skrypt wywołujący (pierwsze miejsce, w którym szuka modułów do użycia przez ten skrypt). Możemy usunąć nazwę samego pliku z końca sys.argv[0](i właśnie to zrobiłem os.path.basename). os.path.joinpo prostu łączy je ze sobą na wiele platform. os.path.realpathupewnia się tylko, czy otrzymamy jakieś dowiązania symboliczne o innych nazwach niż sam skrypt, że nadal otrzymujemy prawdziwą nazwę skryptu.

Nie mam komputera Mac; więc nie przetestowałem tego na jednym. Daj mi znać, czy to działa, jak się wydaje. Testowałem to w systemie Linux (Xubuntu) w Pythonie 3.4. Zwróć uwagę, że wiele rozwiązań tego problemu nie działa na komputerach Mac (ponieważ słyszałem, że __file__nie występuje na komputerach Mac).

Zauważ, że jeśli twój skrypt jest dowiązaniem symbolicznym, poda ci ścieżkę do pliku, do którego prowadzi (a nie ścieżkę dowiązania symbolicznego).

Brōtsyorfuzthrāx
źródło
2

Możesz użyć Pathz pathlibmodułu:

from pathlib import Path

# ...

Path(__file__)

Możesz użyć połączenia, parentaby przejść dalej:

Path(__file__).parent
Gavriel Cohen
źródło
Ta odpowiedź wykorzystuje __file__zmienną, która może być zawodna (nie zawsze pełna ścieżka pliku, nie działa w każdym systemie operacyjnym itp.), Jak często wspominali użytkownicy StackOverflow. Zmiana odpowiedzi na nieuwzględniającą spowoduje mniej problemów i będzie bardziej kompatybilna krzyżowo. Aby uzyskać więcej informacji, zobacz stackoverflow.com/a/33532002/3787376 .
Edward,
@ mrroot5 Ok, więc proszę usunąć komentarz.
Gavriel Cohen
1

Jeśli kod pochodzi z pliku, możesz uzyskać jego pełną nazwę

sys._getframe().f_code.co_filename

Możesz także pobrać nazwę funkcji jako f_code.co_name

Alex Cohn
źródło
0

Po prostu dodaj:

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

Lub:

from sys import *
print(sys.argv[0])
H. Kamran
źródło
0

Moje rozwiązanie to:

import os
print(os.path.dirname(os.path.abspath(__file__)))
ThienSuBS
źródło
-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))
Mohamed.Abdo
źródło
To nie ma sensu. Po pierwsze, '__file__'nie powinien być ciągiem, po drugie, jeśli tak __file__, działałoby to tylko dla pliku, w którym znajduje się ten wiersz kodu, a nie dla pliku, który jest wykonywany?
Andreas Storvik Strauman