L'attribut du module __file__ est-il absolu ou relatif?

107

J'ai du mal à comprendre __file__. D'après ce que je comprends, __file__renvoie le chemin absolu à partir duquel le module a été chargé.

J'ai du mal à produire ceci: j'ai abc.pyune instruction avec une print __file__, exécutée à partir de /d/projects/ python abc.pyretours abc.py. fuyant les /d/retours projects/abc.py. Des raisons pourquoi?

goh
la source
10
Voici ce que Guido a à dire à ce sujet: mail.python.org/pipermail/python-dev/2010-Février/097461.html
kindall
Pertinentes: stackoverflow.com/q/9271464/1959808
Ioannis Filippidis

Réponses:

98

De la documentation :

__file__est le chemin du fichier à partir duquel le module a été chargé, s'il a été chargé à partir d'un fichier. L' __file__attribut n'est pas présent pour les modules C qui sont liés statiquement dans l'interpréteur; pour les modules d'extension chargés dynamiquement à partir d'une bibliothèque partagée, c'est le chemin du fichier de bibliothèque partagée.

Depuis le fil de discussion de la liste de diffusion lié par @kindall dans un commentaire à la question:

Je n'ai pas essayé de reproduire cet exemple particulier, mais la raison en est que nous ne voulons pas avoir à appeler getpwd () à chaque importation ni que nous ne voulons avoir une sorte de variable en cours de traitement pour mettre en cache le répertoire actuel. (getpwd () est relativement lent et peut parfois échouer carrément, et essayer de le mettre en cache présente un certain risque de se tromper.)

Ce que nous faisons à la place, c'est du code dans site.py qui parcourt les éléments de sys.path et les transforme en chemins absolus. Cependant, ce code s'exécute avant que «» ne soit inséré devant sys.path, de sorte que la valeur initiale de sys.path soit «».

Pour le reste, pensez à sys.pathne pas inclure ''.

Donc, si vous êtes en dehors de la partie de sys.pathcelle qui contient le module, vous obtiendrez un chemin absolu . Si vous êtes dans la partie sys.pathqui contient le module, vous obtiendrez un chemin relatif .

Si vous chargez un module dans le répertoire courant et le répertoire courant n'est pas dans sys.path, vous aurez un chemin absolu.

Si vous chargez un module dans le répertoire courant et que le répertoire courant est dans sys.path, vous obtiendrez un chemin relatif.

agf
la source
ainsi cela signifie-t-il que s'il y a un chemin de «» au module, un chemin relatif serait utilisé, sinon un chemin absolu serait utilisé puisque le reste de sys.path est absolu ..
goh
4
Si vous chargez un module dans le répertoire courant et le répertoire courant n'est pas dans sys.path, vous aurez un chemin absolu. Si vous chargez un module dans le répertoire courant et que le répertoire courant est dans sys.path, vous obtiendrez un chemin relatif.
agf
N'oubliez pas, à cette fin, sys.pathn'inclut pas ''.
agf
je l'ai, mais @agf, si j'utilise python /foo/abc.py de / home, je suppose que la partie de sys.path qui contient le module est / home / foo et mon répertoire actuel est / home /, pourquoi est-ce fichier me donne un chemin relatif?
goh
55

__file__est absolu depuis Python 3.4 , sauf lors de l'exécution d'un script directement en utilisant un chemin relatif:

Les __file__attributs de module (et les valeurs associées) doivent désormais toujours contenir des chemins absolus par défaut, à la seule exception du __main__.__file__moment où un script a été exécuté directement en utilisant un chemin relatif. (Contribution de Brett Cannon dans bpo-18416 .)

Je ne sais pas si cela résout les liens symboliques.

Exemple de passage d'un chemin relatif:

$ python script.py
anatoly techtonik
la source
1
Merci. C'est un fait difficile à retracer!
meawoppl
4
Ce n'est pas vrai pour Python 3.4.0 ( Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux). Et les liens symboliques ne sont pas résolus dans mes essais.
Frozen Flame
@FrozenFlame, n'hésitez pas à signaler à bugs.python.org si 3.4.1 ne le corrige pas.
anatoly techtonik
2
Est-ce os.path.realpath(__file__)la bonne façon de résoudre les liens symboliques?
kevinarpe
3
@kevinarpe, stackoverflow.com/questions/3220755/…
anatoly techtonik
16

Exemple simple tardif:

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

Sous Python-2. *, Le deuxième appel détermine de manière incorrecte le en path.abspath(__file__)fonction du répertoire actuel:

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

Comme indiqué par @techtonik, dans Python 3.4+, cela fonctionnera bien car __file__renvoie un chemin absolu.

SimplyKnownAsG
la source
... sauf pour le __main__module, où __file__ peut être un chemin relatif.
0xC0000022L
5

Avec l'aide du courrier de Guido fourni par @kindall, nous pouvons comprendre le processus d'importation standard comme une tentative de trouver le module dans chaque membre de sys.path, et le fichier à la suite de cette recherche (plus de détails dans Modules PyMOTW et importations .). Donc si le module est situé dans un chemin absolu dans sys.pathle résultat est absolu, mais s'il est situé dans un chemin relatif danssys.path le résultat est relatif.

Maintenant, le site.pyfichier de démarrage se charge de ne fournir que le chemin absolu sys.path, sauf l'initiale '', donc si vous ne le modifiez pas par un autre moyen que de définir le PYTHONPATH (dont le chemin est également rendu absolu, avant de préfixersys.path ), vous obtiendrez toujours un absolu chemin, mais lorsque le module est accédé via le répertoire courant.

Maintenant, si vous trompez sys.path d'une manière amusante, vous pouvez obtenir n'importe quoi.

A titre d' exemple , si vous avez un module échantillon foo.pydans /tmp/le code:

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

Si vous entrez dans / tmp, vous obtenez:

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

Lorsque /home/uservous y êtes, si vous ajoutez /tmpvotre, PYTHONPATHvous obtenez:

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

Même si vous ajoutez ../../tmp, il sera normalisé et le résultat est le même.

Mais si au lieu d'utiliser PYTHONPATHvous utilisez directement un chemin amusant, vous obtenez un résultat aussi amusant que la cause.

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

Guido explique dans le fil de discussion cité ci-dessus, pourquoi python n'essaye pas de transformer toutes les entrées en chemins absolus:

nous ne voulons pas avoir à appeler getpwd () à chaque import ... getpwd () est relativement lent et peut parfois échouer carrément,

Donc, votre chemin est utilisé tel quel .

Marcz
la source