Jak odzyskać ścieżkę modułu?

754

Chcę wykryć, czy moduł się zmienił. Teraz korzystanie z inotify jest proste, wystarczy znać katalog, z którego chcesz otrzymywać powiadomienia.

Jak pobrać ścieżkę modułu w Pythonie?

Wesoły
źródło
9
Jeśli nadal szukasz na tej stronie, zaktualizuj poprawną odpowiedź na to . Jest o wiele czystszy niż proponowane rozwiązanie i działa również w przypadkach, gdy __file__nie jest ustawiony.
erikbwork,
3
@ erikb85: to nie tylko czystsze; inspectoparte na rozwiązaniu działa również w execfile()przypadku, gdy __file__cicha nazwa jest niepoprawna.
jfs
import pathlib, module; pathlib.Path(module.__file__).resolve().parentTo
niezależne od

Odpowiedzi:

905
import a_module
print(a_module.__file__)

Poda ścieżkę do załadowanego pliku .pyc, przynajmniej w systemie Mac OS X. Sądzę, że możesz:

import os
path = os.path.abspath(a_module.__file__)

Możesz także spróbować:

path = os.path.dirname(a_module.__file__)

Aby uzyskać katalog modułu.

orestis
źródło
54
odpowiada to, jak uzyskać ścieżkę do modułu, który importujesz, ale nie modułu, w którym się znajdujesz (dla skryptu, który uruchamiasz, __file__nie jest to pełna ścieżka, jest względna). Dla pliku, w którym jestem, musiałem zaimportować kolejny moduł z tego samego katalogu i zrobić, jak pokazano tutaj. Czy ktoś zna wygodniejszy sposób?
Ben Bryant
5
@hbdgaf całkiem pewne, że nie ma czegoś takiego jak wbudowanyself.__file__
Dan Passaro
26
@BenBryant @hbdgaf os.path.dirname(__file__)działa dla mnie dobrze i zwraca ścieżkę abs katalogu modułu.
Niccolò,
9
Próbowałem to zrobić i uzyskać informacje zwrotne: AttributeError: 'module' object has no attribute '__file__'
Dorian Dore,
5
@DorianDore Trochę bałaganiłem moduły i path = module.__path__.__dict__["_path"][0]znalazłem rozwiązanie , ale nie jestem pewien, czy jest przenośny, czy też nie różni się między wersjami Pythona. Działa dla mnie, w przeciwieństwie do tej odpowiedzi, która daje ten sam błąd, a inspectodpowiedź podnosi TypeError: <module 'module' (namespace)> is a built-in module...
Jezor
279

W inspectpythonie jest moduł.

Oficjalna dokumentacja

Moduł inspekcji udostępnia kilka przydatnych funkcji, które pomagają uzyskać informacje o obiektach aktywnych, takich jak moduły, klasy, metody, funkcje, elementy śledzące, obiekty ramek i obiekty kodu. Na przykład może pomóc zbadać zawartość klasy, pobrać kod źródłowy metody, wyodrębnić i sformatować listę argumentów dla funkcji lub uzyskać wszystkie informacje potrzebne do wyświetlenia szczegółowego śledzenia.

Przykład:

>>> import os
>>> import inspect
>>> inspect.getfile(os)
'/usr/lib64/python2.7/os.pyc'
>>> inspect.getfile(inspect)
'/usr/lib64/python2.7/inspect.pyc'
>>> os.path.dirname(inspect.getfile(inspect))
'/usr/lib64/python2.7'
Tomas Tomecek
źródło
7
Możesz użyć, inspectaby uzyskać nazwę bieżącego pliku; patrz stackoverflow.com/a/50905/320036
z0r 24.09.13
5
Google to pytanie wiele razy i to jest najbardziej rozsądna odpowiedź, jaką kiedykolwiek widziałem! Zaktualizuj informacje oinspect.currentframe()
erikbwork,
inspect.getfile()podejście nie działa z modułem _io, ale współpracuje z modułem io.
smwikipedia,
70

Jak powiedziano w innych odpowiedziach, najlepszym sposobem na to jest __file__(pokazane ponownie poniżej). Istnieje jednak ważne zastrzeżenie, które __file__NIE istnieje, jeśli moduł jest uruchamiany samodzielnie (tj. As __main__).

Załóżmy na przykład, że masz dwa pliki (oba na PYTHONPATH):

#/path1/foo.py
import bar
print(bar.__file__)

i

#/path2/bar.py
import os
print(os.getcwd())
print(__file__)

Uruchomienie foo.py da wynik:

/path1        # "import bar" causes the line "print(os.getcwd())" to run
/path2/bar.py # then "print(__file__)" runs
/path2/bar.py # then the import statement finishes and "print(bar.__file__)" runs

JEDNAK jeśli spróbujesz uruchomić bar.py na własną rękę, otrzymasz:

/path2                              # "print(os.getcwd())" still works fine
Traceback (most recent call last):  # but __file__ doesn't exist if bar.py is running as main
  File "/path2/bar.py", line 3, in <module>
    print(__file__)
NameError: name '__file__' is not defined 

Mam nadzieję że to pomoże. To zastrzeżenie kosztowało mnie dużo czasu i zamieszania podczas testowania innych przedstawionych rozwiązań.

Mcstrother
źródło
5
W takim przypadku możesz użyć sys.argv [0] zamiast pliku .
Jimothy
4
Czy to zastrzeżenie dotyczące konkretnej wersji? W wersjach 2.6 i 2.7 z powodzeniem korzystam z pliku , który działa na pliku o nazwie __ == '__ main '. Jedyny przypadek niepowodzenia, jaki widziałem, to „python -c 'print file '”. Dodam, że czasami plik może mieć wartość <stdin>, co dzieje się, gdy IDE, takie jak emacs, wykonują bieżący bufor.
Paul Du Bois,
1
Zwróć uwagę, że wiodące i końcowe znaki „__” pogrubiają słowo, więc pamiętaj o tym podczas czytania poprzednich komentarzy :-P
Paul Du Bois
1
@PaulDuBois Możesz go otoczyć za pomocą tików tylnych: ` __file__` staje się__file__
fncomp
1
Jak zdobyć NameError? @Paul Du Bois: Próbowałem Python 2.3-3.4 i __file__definiuje jednak uruchomić plik Pythona: python a.py, python -ma, ./a.py.
jfs
43

Spróbuję również rozwiązać kilka wariantów tego pytania:

  1. znalezienie ścieżki wywoływanego skryptu
  2. znalezienie ścieżki aktualnie wykonywanego skryptu
  3. znajdowanie katalogu wywoływanego skryptu

(Niektóre z tych pytań zostały zadane na SO, ale zostały zamknięte jako duplikaty i przekierowane tutaj).

Ostrzeżenia dotyczące używania __file__

W przypadku zaimportowanego modułu:

import something
something.__file__ 

zwróci bezwzględną ścieżkę modułu. Biorąc jednak pod uwagę następujący skrypt foo.py:

#foo.py
print '__file__', __file__

Wywołanie go za pomocą „python foo.py” zwróci po prostu „foo.py”. Jeśli dodasz shebang:

#!/usr/bin/python 
#foo.py
print '__file__', __file__

i nazwij to używając ./foo.py, zwróci „./foo.py”. Wywołanie go z innego katalogu (np. Umieść foo.py w pasku katalogu), a następnie wywołanie jednego z nich

python bar/foo.py

lub dodanie shebang i bezpośrednie wykonanie pliku:

bar/foo.py

zwróci „bar / foo.py” ( ścieżka względna ).

Znajdowanie katalogu

Teraz stamtąd, aby uzyskać katalog, os.path.dirname(__file__)może być również trudne. Przynajmniej w moim systemie zwraca pusty ciąg, jeśli wywołasz go z tego samego katalogu co plik. dawny.

# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)

wyświetli:

__file__ is: foo.py
os.path.dirname(__file__) is: 

Innymi słowy, zwraca pusty ciąg, więc nie wydaje się wiarygodny, jeśli chcesz go użyć dla bieżącego pliku (w przeciwieństwie do pliku importowanego modułu). Aby obejść ten problem, możesz zawrzeć go w wywołaniu abspath:

# foo.py
import os
print 'os.path.abspath(__file__) is:', os.path.abspath(__file__)
print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__))

która generuje coś takiego:

os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar

Zauważ, że abspath () NIE rozpoznaje dowiązań symbolicznych. Jeśli chcesz to zrobić, użyj zamiast tego realpath (). Na przykład utworzenie dowiązania symbolicznego file_import_testing_link wskazujące na file_import_testing.py, o następującej treści:

import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)

wykonanie spowoduje wydrukowanie ścieżek bezwzględnych, takich jak:

abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py

file_import_tporting_testing -> file_import_testing.py

Korzystanie z inspekcji

@SummerBreeze wspomina o module inspekcji .

Wydaje się, że działa to dobrze i jest dość zwięzłe w przypadku importowanych modułów:

import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)

posłusznie zwraca absolutną ścieżkę. Jednak aby znaleźć ścieżkę aktualnie wykonywanego skryptu, nie widziałem sposobu, aby go użyć.

jpgeek
źródło
1
inspect.getfile (os) jest taki sam jak plik os .__ z kodu: def getfile (obiekt): "" "Sprawdź, w którym źródle lub skompilowanym pliku zdefiniowano obiekt." "" ismodule (obiekt): if hasattr (obiekt, „ plik ”): zwraca obiekt .__ plik__
idanzalz
4
Możesz użyć, inspect.getfile(inspect.currentframe())aby uzyskać ścieżkę do aktualnie uruchomionego skryptu.
jbochi
33

Nie rozumiem, dlaczego nikt o tym nie mówi, ale dla mnie najprostszym rozwiązaniem jest użycie imp.find_module („nazwa modułu”) (dokumentacja tutaj ):

import imp
imp.find_module("os")

Daje krotkę ze ścieżką w drugiej pozycji:

(<open file '/usr/lib/python2.7/os.py', mode 'U' at 0x7f44528d7540>,
'/usr/lib/python2.7/os.py',
('.py', 'U', 1))

Zaletą tej metody w porównaniu z metodą „inspekcji” jest to, że nie trzeba importować modułu, aby działał, i można użyć ciągu wejściowego. Przydatne przy sprawdzaniu modułów wywoływanych na przykład w innym skrypcie.

EDYCJA :

W python3 importlibmoduł powinien wykonać:

Dokument z importlib.util.find_spec:

Zwraca specyfikację dla określonego modułu.

Najpierw sprawdzane jest sys.modules, aby sprawdzić, czy moduł został już zaimportowany. Jeśli tak, to sys.modules [nazwa]. specyfikacja jest zwracana. Jeśli tak się stanie, zostanie ustawiona wartość Brak, wówczas zgłaszany jest błąd ValueError. Jeśli modułu nie ma w sys.modules, to sys.meta_path jest przeszukiwany w celu znalezienia odpowiedniej specyfikacji z wartością „path” podaną wyszukiwarkom. Żadne nie jest zwracane, jeśli nie można znaleźć specyfikacji.

Jeśli nazwa dotyczy submodułu (zawiera kropkę), moduł nadrzędny jest importowany automatycznie.

Argumenty nazwy i pakietu działają tak samo jak importlib.import_module (). Innymi słowy, względne nazwy modułów (z wiodącymi kropkami) działają.

Plazma Binturong
źródło
2
imp NIE jest amortyzowane w Pythonie 2 (aktualna wersja 2.7.13). imp jest amortyzowany w Pythonie 3 od wersji 3.4. zamiast tego należy użyć importlib w Pythonie 3. Podoba mi się to rozwiązanie, ponieważ działa nawet wtedy, gdy faktyczny import się nie powiedzie (np. Ponieważ moduł 64-bitowy dla silnika 32-bitowego)
mdew
I NAPRAWDĘ fajnie, gdy próbuje znaleźć ścieżkę 64-bitowego narzędzia sqlite3 i import kończy się niepowodzeniem. Doskonały.
rahvin_t,
2
importlib.machinery.PathFinder().find_module("os").get_filename()Najkrótsza alternatywa dla imp.find_moduleznalazłem w Python3 +. Jeśli ktoś szuka użycia importlib.util.find_spec.
Torxed
Ta metoda działa bez importowania faktycznego modułu, co jest świetne, ponieważ używam go do ustalenia, która wersja modułu jest importowana z udostępnionego komputera.
Gouda
19

To było banalne.

Każdy moduł ma __file__zmienną, która pokazuje ścieżkę względną z miejsca, w którym jesteś teraz.

Dlatego uzyskanie katalogu, w którym moduł będzie powiadamiał o tym, jest proste:

os.path.dirname(__file__)
Wesoły
źródło
14
Prawie, ale nie całkiem dobrze - plik nie jest „w stosunku do tego, gdzie jesteś teraz”; gdy jest względny (który będzie tylko wtedy, gdy istnieją ścieżki względne w sys.path), to jest relatywny do tego, gdzie byłeś, gdy moduł został załadowany .
Charles Duffy,
17
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)
Vinoth
źródło
1
nie działa na moim pythonie dla Linuksa 2.6, ponieważ __file__jest to po prostu katalog / test.py, abspath wymaga cwd do uzupełnienia ścieżki, co nie jest pożądanym rezultatem, ale jeśli zaimportujesz moduł, to m.__file__da pożądany wynik.
Ben Bryant,
10
import module
print module.__path__

Pakiety obsługują jeszcze jeden specjalny atrybut __path__. Jest to inicjowane jako lista zawierająca nazwę katalogu zawierającego pakiet __init__.pyprzed wykonaniem kodu w tym pliku. Ta zmienna może być modyfikowana; wpływa to na przyszłe wyszukiwania modułów i podpakietów zawartych w pakiecie.

Chociaż ta funkcja nie jest często potrzebna, można jej użyć do rozszerzenia zestawu modułów znajdujących się w pakiecie.

Źródło

Lukas Greblikas
źródło
9

Narzędzie wiersza polecenia

Możesz go dostosować do narzędzia wiersza poleceń,

python-which <package name>

wprowadź opis zdjęcia tutaj


Stwórz /usr/local/bin/python-which

#!/usr/bin/env python

import importlib
import os
import sys

args = sys.argv[1:]
if len(args) > 0:
    module = importlib.import_module(args[0])
    print os.path.dirname(module.__file__)

Zrób to

sudo chmod +x /usr/local/bin/python-which
Jossef Harush
źródło
7

Spędziłem więc sporo czasu próbując to zrobić za pomocą py2exe. Problem polegał na uzyskaniu podstawowego folderu skryptu, niezależnie od tego, czy był on uruchamiany jako skrypt Pythona, czy jako plik wykonywalny py2exe. Również, aby działało niezależnie od tego, czy było uruchamiane z bieżącego folderu, innego folderu lub (to było najtrudniejsze) ze ścieżki systemowej.

Ostatecznie zastosowałem to podejście, używając sys.frozen jako wskaźnika działania w py2exe:

import os,sys
if hasattr(sys,'frozen'): # only when running in py2exe this exists
    base = sys.prefix
else: # otherwise this is a regular python script
    base = os.path.dirname(os.path.realpath(__file__))
uri
źródło
7

możesz po prostu zaimportować moduł, a następnie kliknąć jego nazwę, a uzyskasz pełną ścieżkę

>>> import os
>>> os
<module 'os' from 'C:\\Users\\Hassan Ashraf\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\os.py'>
>>>
Hassan Ashraf
źródło
4

Jeśli chcesz pobrać ścieżkę katalogu głównego pakietu z dowolnego z jego modułów, działają następujące funkcje (testowane w Pythonie 3.6):

from . import __path__ as ROOT_PATH
print(ROOT_PATH)

Do głównej __init__.pyścieżki można również odwołać się, używając __file__zamiast niej.

Mam nadzieję że to pomoże!

fr_andres
źródło
3

Jeśli jedynym zastrzeżeniem dotyczącym używania __file__jest bieżący, względny katalog jest pusty (tzn. Gdy działa jako skrypt z tego samego katalogu, w którym znajduje się skrypt), wówczas trywialnym rozwiązaniem jest:

import os.path
mydir = os.path.dirname(__file__) or '.'
full  = os.path.abspath(mydir)
print __file__, mydir, full

A wynik:

$ python teste.py 
teste.py . /home/user/work/teste

Sztuką jest or '.'po dirname()rozmowie. Ustawia katalog jako ., co oznacza katalog bieżący i jest prawidłowym katalogiem dla każdej funkcji związanej ze ścieżką.

Dlatego używanie abspath()nie jest tak naprawdę potrzebne. Ale jeśli i tak go użyjesz, sztuczka nie jest potrzebna: abspath()akceptuje puste ścieżki i poprawnie interpretuje go jako bieżący katalog.

MestreLion
źródło
Lepiej zamień kolejność i po prostu użyj, os.path.dirname(os.path.abspath(__file__))ponieważ wtedy nie musisz się martwić o ścieżki względne. Jedna mniejsza szansa na błędy.
szali
3

Chciałbym przyczynić się do jednego wspólnego scenariusza (w Pythonie 3) i zbadać kilka podejść do niego.

Wbudowana funkcja open () akceptuje ścieżkę względną lub bezwzględną jako swój pierwszy argument. Ścieżka względna jest traktowana jako względna do bieżącego katalogu roboczego, dlatego zaleca się podanie bezwzględnej ścieżki do pliku.

Mówiąc prosto, jeśli uruchomić skrypt z następującego kodu, to nie gwarantuje, że example.txtplik zostanie utworzony w tym samym katalogu, w którym znajduje się plik skryptu:

with open('example.txt', 'w'):
    pass

Aby naprawić ten kod, musimy uzyskać ścieżkę do skryptu i uczynić go absolutnym. Aby zapewnić absolutną ścieżkę, po prostu używamy funkcji os.path.realpath () . Aby uzyskać ścieżkę do skryptu, istnieje kilka typowych funkcji, które zwracają różne wyniki ścieżki:

  • os.getcwd()
  • os.path.realpath('example.txt')
  • sys.argv[0]
  • __file__

Obie funkcje os.getcwd () i os.path.realpath () zwracają wyniki ścieżki na podstawie bieżącego katalogu roboczego . Generalnie nie to, czego chcemy. Pierwszym elementem listy sys.argv jest ścieżka skryptu głównego (uruchamianego skryptu) bez względu na to, czy wywołujesz listę w samym skrypcie głównym, czy w którymkolwiek z jego modułów. W niektórych sytuacjach może się przydać. __File__ Zmienna zawiera ścieżkę do modułu, z którego została wywołana.


Poniższy kod poprawnie tworzy plik example.txtw tym samym katalogu, w którym znajduje się skrypt:

filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')

with open(filepath, 'w'):
    pass
Jeyekomon
źródło
3

Jeśli chcesz poznać ścieżkę bezwzględną ze skryptu, możesz użyć obiektu Path :

from pathlib import Path

print(Path().absolute())
print(Path().resolve('.'))
print(Path().cwd())

metoda cwd ()

Zwraca nowy obiekt ścieżki reprezentujący bieżący katalog (zwrócony przez os.getcwd ())

metoda resol ()

Uczyń ścieżkę absolutną, rozwiązując wszelkie dowiązania symboliczne. Zwracany jest nowy obiekt ścieżki:

Vlad Bezden
źródło
1

Z modułów pakietu Pythona musiałem odwoływać się do pliku znajdującego się w tym samym katalogu co pakiet. Dawny.

some_dir/
  maincli.py
  top_package/
    __init__.py
    level_one_a/
      __init__.py
      my_lib_a.py
      level_two/
        __init__.py
        hello_world.py
    level_one_b/
      __init__.py
      my_lib_b.py

Tak więc powyżej musiałem wywołać maincli.py z modułu my_lib_a.py wiedząc, że top_package i maincli.py znajdują się w tym samym katalogu. Oto jak mogę uzyskać ścieżkę do maincli.py:

import sys
import os
import imp


class ConfigurationException(Exception):
    pass


# inside of my_lib_a.py
def get_maincli_path():
    maincli_path = os.path.abspath(imp.find_module('maincli')[1])
    # top_package = __package__.split('.')[0]
    # mod = sys.modules.get(top_package)
    # modfile = mod.__file__
    # pkg_in_dir = os.path.dirname(os.path.dirname(os.path.abspath(modfile)))
    # maincli_path = os.path.join(pkg_in_dir, 'maincli.py')

    if not os.path.exists(maincli_path):
        err_msg = 'This script expects that "maincli.py" be installed to the '\
        'same directory: "{0}"'.format(maincli_path)
        raise ConfigurationException(err_msg)

    return maincli_path

Na podstawie postów opublikowanych przez PlasmaBinturong zmodyfikowałem kod.

Al Conrad
źródło
1

Jeśli chcesz to zrobić dynamicznie w „programie”, wypróbuj ten kod:
Chodzi mi o to, że możesz nie znać dokładnej nazwy modułu, aby go „zakodować na stałe”. Może być wybrany z listy lub może nie być uruchomiony, aby użyć __pliku__.

(Wiem, nie będzie działać w Pythonie 3)

global modpath
modname = 'os' #This can be any module name on the fly
#Create a file called "modname.py"
f=open("modname.py","w")
f.write("import "+modname+"\n")
f.write("modpath = "+modname+"\n")
f.close()
#Call the file with execfile()
execfile('modname.py')
print modpath
<module 'os' from 'C:\Python27\lib\os.pyc'>

Próbowałem pozbyć się problemu „globalnego”, ale znalazłem przypadki, w których to nie działało. Myślę, że można wykonać emulację „execfile ()” w Pythonie 3 Ponieważ jest to program, można go łatwo umieścić w metodzie lub module ponowne użycie.

Robin Randall
źródło
0

Jeśli zainstalowałeś go za pomocą pipa, „pip show” działa świetnie („Lokalizacja”)

$ pip pokaż detektor2

Name: detectron2
Version: 0.1
Summary: Detectron2 is FAIR next-generation research platform for object detection and segmentation.
Home-page: https://github.com/facebookresearch/detectron2
Author: FAIR
Author-email: None
License: UNKNOWN
Location: /home/ubuntu/anaconda3/envs/pytorch_p36/lib/python3.6/site-packages
Requires: yacs, tabulate, tqdm, pydot, tensorboard, Pillow, termcolor, future, cloudpickle, matplotlib, fvcore
Javi
źródło
0

Oto krótki skrypt bash na wypadek, gdyby był przydatny dla każdego. Chcę tylko móc ustawić zmienną środowiskową, aby móc pushdkodować.

#!/bin/bash
module=${1:?"I need a module name"}

python << EOI
import $module
import os
print os.path.dirname($module.__file__)
EOI

Przykład powłoki:

[root@sri-4625-0004 ~]# export LXML=$(get_python_path.sh lxml)
[root@sri-4625-0004 ~]# echo $LXML
/usr/lib64/python2.7/site-packages/lxml
[root@sri-4625-0004 ~]#
ryjówka
źródło