L'attributo __file__ del modulo è assoluto o relativo?


107

Ho problemi a capire __file__. Da quanto ho capito, __file__restituisce il percorso assoluto da cui è stato caricato il modulo.

Ho problemi a produrre questo: ho abc.pyun'istruzione con una dichiarazione print __file__, che va dai /d/projects/ python abc.pyritorni abc.py. scappando dai /d/ritorni projects/abc.py. Qualche motivo?



Risposte:


98

Dalla documentazione :

__file__è il percorso del file da cui è stato caricato il modulo, se è stato caricato da un file. L' __file__attributo non è presente per i moduli C che sono collegati staticamente nell'interprete; per i moduli di estensione caricati dinamicamente da una libreria condivisa, è il percorso del file della libreria condivisa.

Dal thread della mailing list collegato da @kindall in un commento alla domanda:

Non ho provato a riprodurre questo particolare esempio, ma il motivo è che non vogliamo dover chiamare getpwd () su ogni importazione né vogliamo avere una sorta di variabile in-process per memorizzare nella cache la directory corrente. (getpwd () è relativamente lento e talvolta può fallire del tutto, e provare a memorizzarlo nella cache ha un certo rischio di sbagliarsi.)

Quello che facciamo invece è il codice in site.py che passa sopra gli elementi di sys.path e li trasforma in percorsi assoluti. Tuttavia, questo codice viene eseguito prima che "" venga inserito all'inizio di sys.path, in modo che il valore iniziale di sys.path sia "".

Per il resto, considera di sys.pathnon includere ''.

Quindi, se sei fuori dalla parte sys.pathche contiene il modulo, otterrai un percorso assoluto . Se ti trovi all'interno della parte sys.pathche contiene il modulo, otterrai un percorso relativo .

Se carichi un modulo nella directory corrente e la directory corrente non è presente sys.path, otterrai un percorso assoluto.

Se carichi un modulo nella directory corrente e la directory corrente è in sys.path, otterrai un percorso relativo.


quindi significa che se esiste un percorso da "" al modulo, verrebbe utilizzato un percorso relativo, altrimenti un percorso assoluto poiché il resto di sys.path è assoluto ..
goh

4
Se carichi un modulo nella directory corrente e la directory corrente non è presente sys.path, otterrai un percorso assoluto. Se carichi un modulo nella directory corrente e la directory corrente è in sys.path, otterrai un percorso relativo.
agf

Ricorda, per questo scopo, sys.pathnon include ''.
agf

capito, ma @agf, se uso python /foo/abc.py da / home, suppongo che la parte di sys.path che contiene il modulo sia / home / foo e la mia directory corrente è / home /, perché stampa file mi da un percorso relativo?
goh

55

__file__è assoluto a partire da Python 3.4 , tranne quando si esegue uno script direttamente utilizzando un percorso relativo:

Gli __file__attributi del modulo (e i valori correlati) dovrebbero ora contenere sempre percorsi assoluti per impostazione predefinita, con la sola eccezione di __main__.__file__quando uno script è stato eseguito direttamente utilizzando un percorso relativo. (Contributo di Brett Cannon in bpo-18416 .)

Non sono sicuro che risolva i collegamenti simbolici.

Esempio di passaggio di un percorso relativo:

$ python script.py

1
Grazie. Questo è un fatto difficile da rintracciare!
meawoppl

4
Questo non è vero per Python 3.4.0 ( Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux). E i collegamenti simbolici non vengono risolti nelle mie prove.
Frozen Flame

@FrozenFlame, sentiti libero di segnalare a bugs.python.org se 3.4.1 non lo risolve.
anatoly techtonik

2
È os.path.realpath(__file__)il modo corretto per risolvere i collegamenti simbolici?
kevinarpe


16

Tardo semplice esempio:

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

In Python-2. *, La seconda chiamata determina in modo errato il path.abspath(__file__)basato sulla directory corrente:

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

Come notato da @techtonik, in Python 3.4+, questo funzionerà bene poiché __file__restituisce un percorso assoluto.


... ad eccezione del __main__modulo, dove __file__ può essere un percorso relativo.
0xC0000022L

5

Con l'aiuto della posta di Guido fornita da @kindall, possiamo comprendere il processo di importazione standard come il tentativo di trovare il modulo in ogni membro di sys.pathe il file come risultato di questa ricerca (maggiori dettagli in Moduli e importazioni PyMOTW .). Quindi se il modulo si trova in un percorso assoluto sys.pathil risultato è assoluto, ma se si trova in un percorso relativo sys.pathil risultato è relativo.

Ora il site.pyfile di avvio si occupa di fornire solo il percorso assoluto sys.path, eccetto quello iniziale '', quindi se non lo cambi con altri mezzi che impostando il PYTHONPATH (il cui percorso è anche reso assoluto, prima del prefisso sys.path), otterrai sempre un assoluto path, ma quando si accede al modulo tramite la directory corrente.

Ora, se ingannate sys.path in un modo divertente, potete ottenere qualsiasi cosa.

Come esempio, se si dispone di un modulo di esempio foo.pyin /tmp/con il codice:

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

Se vai in / tmp ottieni:

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

Quando /home/usersei dentro , se aggiungi /tmpil PYTHONPATHtuo ottieni:

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

Anche se aggiungi ../../tmp, verrà normalizzato e il risultato è lo stesso.

Ma se invece di PYTHONPATHusare direttamente un percorso divertente ottieni un risultato divertente quanto la causa.

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

Guido spiega nel thread sopra citato, perché python non tenta di trasformare tutte le voci in percorsi assoluti:

non vogliamo dover chiamare getpwd () su ogni importazione .... getpwd () è relativamente lento e talvolta può fallire completamente,

Quindi il tuo percorso viene utilizzato così com'è .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.