Czy atrybut __file__ modułu jest bezwzględny czy względny?

107

Mam problem ze zrozumieniem __file__. Z tego co rozumiem __file__zwraca bezwzględną ścieżkę, z której moduł został załadowany.

Mam problem z utworzeniem tego: mam abc.pyjedno oświadczenie print __file__, biegnące od /d/projects/ python abc.pyzwrotów abc.py. ucieczka przed /d/zwrotami projects/abc.py. Jakieś powody, dlaczego?

goh
źródło
10
Oto, co Guido ma do powiedzenia na ten temat: mail.python.org/pipermail/python-dev/2010-Feb February
kindall
Trafne: stackoverflow.com/q/9271464/1959808
Ioannis Filippidis

Odpowiedzi:

98

Z dokumentacji :

__file__jest ścieżką do pliku, z którego został załadowany moduł, jeśli został załadowany z pliku. __file__Cecha nie występuje w przypadku modułów C statycznie połączonych w interpreter; w przypadku modułów rozszerzeń ładowanych dynamicznie z biblioteki współdzielonej jest to ścieżka do pliku biblioteki współdzielonej.

Z wątku listy mailingowej, do którego prowadzi @kindall w komentarzu do pytania:

Nie próbowałem odtworzyć tego konkretnego przykładu, ale powodem jest to, że nie chcemy wywoływać getpwd () przy każdym imporcie ani nie chcemy mieć jakiejś zmiennej w procesie do buforowania bieżącego katalogu. (getpwd () jest stosunkowo powolne i czasami może się nie powieść, a próba zapisania jej w pamięci podręcznej wiąże się z pewnym ryzykiem błędu.)

Zamiast tego robimy kod w site.py, który przechodzi przez elementy sys.path i zamienia je w ścieżki absolutne. Jednak ten kod działa, zanim „” zostanie wstawiony na początku sys.path, tak więc początkowa wartość sys.path to „”.

W pozostałej części sys.pathnie uwzględniaj ''.

Tak więc, jeśli jesteś poza częścią, sys.pathktóra zawiera moduł, otrzymasz bezwzględną ścieżkę . Jeśli jesteś wewnątrz części sys.pathzawierającej moduł, otrzymasz ścieżkę względną .

Jeśli załadować moduł w bieżącym katalogu, a bieżący katalog nie jest w sys.path, dostaniesz ścieżki bezwzględnej.

Jeśli załadujesz moduł w bieżącym katalogu, a bieżący katalog się w nim znajdujesys.path , otrzymasz ścieżkę względną.

agf
źródło
tak nie oznacza to, że jeśli istnieje ścieżka ze „” do modułu względnym kierunkiem może być stosowane, jeśli nie absolutna ścieżek będzie stosowane od pozostałej sys.path są absolutne ..
GOH
4
Jeśli załadować moduł w bieżącym katalogu, a bieżący katalog nie jest w sys.path, dostaniesz ścieżki bezwzględnej. Jeśli załadujesz moduł w bieżącym katalogu, a bieżący katalog się w nim znajdujesys.path , otrzymasz ścieżkę względną.
agf
Pamiętaj, że w tym celu sys.pathnie zawiera ''.
agf
rozumiem, ale @agf, jeśli używam pythona /foo/abc.py z / home, przypuszczam, że część sys.path zawierająca moduł to / home / foo, a mój bieżący katalog to / home /, dlaczego drukuje plik daje mi ścieżkę względną?
goh
55

__file__jest bezwzględne od Pythona 3.4 , z wyjątkiem wykonywania skryptu bezpośrednio przy użyciu ścieżki względnej:

__file__Atrybuty modułu (i powiązane wartości) powinny teraz domyślnie zawierać ścieżki bezwzględne, z jedynym wyjątkiem sytuacji, __main__.__file__gdy skrypt został wykonany bezpośrednio przy użyciu ścieżki względnej. ( Nadesłane przez Brett Cannon w bpo-18416 .)

Nie jestem pewien, czy rozwiązuje jednak linki symboliczne.

Przykład przekazania ścieżki względnej:

$ python script.py
anatoly techtonik
źródło
1
Dzięki. Trudno to wyśledzić!
meawoppl
4
Nie dotyczy to Pythona 3.4.0 ( Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux). A linki symboliczne nie są rozwiązywane w moich próbach.
Frozen Flame
@FrozenFlame, nie krępuj się zgłosić do bugs.python.org, jeśli 3.4.1 tego nie naprawi.
anatoly techtonik
2
Czy os.path.realpath(__file__)właściwy sposób rozwiązywania dowiązań symbolicznych?
kevinarpe
3
@kevinarpe, stackoverflow.com/questions/3220755/ ...
anatoly techtonik
16

Późny prosty przykład:

from os import path, getcwd, chdir

def print_my_path():
    print('cwd:     {}'.format(getcwd()))
    print('__file__:{}'.format(__file__))
    print('abspath: {}'.format(path.abspath(__file__)))

print_my_path()

chdir('..')

print_my_path()

W Python-2. * Drugie wywołanie nieprawidłowo określa na path.abspath(__file__)podstawie bieżącego katalogu:

cwd:     C:\codes\py
__file__:cwd_mayhem.py
abspath: C:\codes\py\cwd_mayhem.py
cwd:     C:\codes
__file__:cwd_mayhem.py
abspath: C:\codes\cwd_mayhem.py

Jak zauważył @techtonik, w Pythonie 3.4+ będzie to działać dobrze, ponieważ __file__zwraca ścieżkę bezwzględną.

SimplyKnownAsG
źródło
... z wyjątkiem __main__modułu, gdzie __file__ może być ścieżką względną.
0xC0000022L
5

Z pomocą poczty Guido dostarczonej przez @kindall, możemy zrozumieć standardowy proces importu jako próbę znalezienia modułu w każdym elemencie sys.pathi pliku w wyniku tego wyszukiwania (więcej szczegółów w PyMOTW Modules and Imports .). Więc jeśli moduł znajduje się na ścieżce sys.pathbezwzględnej, wynik jest bezwzględny, ale jeśli znajduje się na ścieżce względnej, sys.pathwynik jest względny.

Teraz site.pyplik startowy dba o dostarczenie tylko ścieżki bezwzględnej w sys.path, poza początkową '', więc jeśli nie zmienisz jej w inny sposób niż ustawienie PYTHONPATH (której ścieżka również jest bezwzględna, przed prefiksowaniem sys.path), zawsze otrzymasz bezwzględną path, ale gdy dostęp do modułu odbywa się za pośrednictwem bieżącego katalogu.

Jeśli oszukasz sys.path w zabawny sposób, możesz uzyskać wszystko.

Jako przykład, jeśli masz moduł próbki foo.pyw /tmp/z kodem:

import sys
print(sys.path)
print (__file__)

Jeśli wejdziesz do / tmp, otrzymasz:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
./foo.py

Po wejściu /home/user, jeśli dodasz /tmpswoje PYTHONPATH, otrzymasz:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
/tmp/foo.py

Nawet jeśli dodasz ../../tmp, zostanie znormalizowany, a wynik będzie taki sam.

Ale jeśli zamiast użyć PYTHONPATH, użyjesz bezpośrednio jakiejś zabawnej ścieżki, otrzymasz wynik tak zabawny, jak przyczyna.

>>> import sys
>>> sys.path.append('../../tmp')
>>> import foo
['', '/usr/lib/python3.3', .... , '../../tmp']
../../tmp/foo.py

Guido wyjaśnia w cytowanym powyżej wątku, dlaczego Python nie próbuje przekształcić wszystkich wpisów w ścieżki absolutne:

nie chcemy wywoływać getpwd () przy każdym imporcie ... getpwd () jest stosunkowo powolne i czasami może się nie powieść,

Więc twoja ścieżka jest używana taka, jaka jest .

Marcz
źródło