Ist das Modul __file__ Attribut absolut oder relativ?

106

Ich habe Probleme beim Verstehen __file__. Soweit ich weiß, wird __file__der absolute Pfad zurückgegeben, von dem das Modul geladen wurde.

Ich habe Probleme, dies zu produzieren: Ich habe eine abc.pymit einer Anweisung print __file__, die von /d/projects/ python abc.pyRückgaben ausgeht abc.py. läuft von /d/Retouren projects/abc.py. Irgendwelche Gründe warum?

goh
quelle
10
Hier ist, was Guido dazu zu sagen hat: mail.python.org/pipermail/python-dev/2010-February/097461.html
bitte alle
Relevant: stackoverflow.com/q/9271464/1959808
Ioannis Filippidis

Antworten:

98

Aus der Dokumentation :

__file__ist der Pfadname der Datei, aus der das Modul geladen wurde, wenn es aus einer Datei geladen wurde. Das __file__Attribut ist für C-Module, die statisch mit dem Interpreter verknüpft sind, nicht vorhanden. Bei Erweiterungsmodulen, die dynamisch aus einer gemeinsam genutzten Bibliothek geladen werden, ist dies der Pfadname der gemeinsam genutzten Bibliotheksdatei.

Aus dem Mailinglisten-Thread, der von @kindall in einem Kommentar zur Frage verlinkt wurde:

Ich habe nicht versucht, dieses spezielle Beispiel erneut zu erstellen, aber der Grund dafür ist, dass wir nicht bei jedem Import getpwd () aufrufen müssen und auch keine In-Process-Variable zum Zwischenspeichern des aktuellen Verzeichnisses benötigen. (getpwd () ist relativ langsam und kann manchmal sofort fehlschlagen. Wenn Sie versuchen, es zwischenzuspeichern, besteht ein gewisses Risiko, dass Sie falsch liegen.)

Was wir stattdessen tun, ist Code in site.py, der über die Elemente von sys.path geht und sie in absolute Pfade umwandelt. Dieser Code wird jedoch ausgeführt, bevor '' vor sys.path eingefügt wird, sodass der Anfangswert von sys.path '' ist.

Im Übrigen sollten Sie sys.pathnicht berücksichtigen ''.

Wenn Sie sich also außerhalb des Teils sys.pathbefinden, der das Modul enthält, erhalten Sie einen absoluten Pfad . Wenn Sie sich in dem Teil sys.pathbefinden, der das Modul enthält, erhalten Sie einen relativen Pfad .

Wenn Sie ein Modul im aktuellen Verzeichnis zu laden, und das aktuelle Verzeichnis ist nicht in sys.path, werden Sie einen absoluten Pfad bekommen.

Wenn Sie ein Modul im aktuellen Verzeichnis laden, und das aktuelle Verzeichnis ist in sys.path, werden Sie einen relativen Pfad bekommen.

agf
quelle
Dies bedeutet auch, dass bei einem Pfad von '' zum Modul ein relativer Pfad verwendet wird, wenn kein absoluter Pfad verwendet wird, da der Rest von sys.path absolut ist.
goh
4
Wenn Sie ein Modul im aktuellen Verzeichnis zu laden, und das aktuelle Verzeichnis ist nicht in sys.path, werden Sie einen absoluten Pfad bekommen. Wenn Sie ein Modul im aktuellen Verzeichnis laden, und das aktuelle Verzeichnis ist in sys.path, werden Sie einen relativen Pfad bekommen.
Agf
Denken Sie daran, zu diesem Zweck sys.pathnicht enthalten ''.
Agf
Ich habe es verstanden, aber @agf, wenn ich Python /foo/abc.py von / home aus verwende, ist der Teil von sys.path, der das Modul enthält, / home / foo und mein aktuelles Verzeichnis / home /. Warum wird gedruckt? Datei gibt mir einen relativen Pfad?
Goh
55

__file__ist seit Python 3.4 absolut , außer wenn ein Skript direkt über einen relativen Pfad ausgeführt wird:

Modulattribute __file__(und zugehörige Werte) sollten jetzt standardmäßig immer absolute Pfade enthalten, mit der einzigen Ausnahme, __main__.__file__wenn ein Skript direkt unter Verwendung eines relativen Pfads ausgeführt wurde. (Beitrag von Brett Cannon in bpo-18416 .)

Ich bin mir nicht sicher, ob Symlinks aufgelöst werden.

Beispiel für die Übergabe eines relativen Pfads:

$ python script.py
anatoly techtonik
quelle
1
Vielen Dank. Dies ist eine schwer zu ermittelnde Tatsache!
Meawoppl
4
Dies gilt nicht für Python 3.4.0 ( Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux). Und Symlinks werden in meinen Versuchen nicht gelöst.
Frozen Flame
@FrozenFlame, zögern Sie nicht, bugs.python.org Bericht zu erstatten, wenn 3.4.1 dies nicht behebt.
Anatoly Techtonik
2
Ist os.path.realpath(__file__)der richtige Weg, um symbolische Links aufzulösen?
Kevinarpe
3
@ kevinarpe, stackoverflow.com/questions/3220755/…
anatoly techtonik
16

Spätes einfaches Beispiel:

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

Unter Python-2. * Bestimmt der zweite Aufruf fälschlicherweise das path.abspath(__file__)basierend auf dem aktuellen Verzeichnis:

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

Wie von @techtonik in Python 3.4+ festgestellt, funktioniert dies einwandfrei, da __file__ein absoluter Pfad zurückgegeben wird.

SimplyKnownAsG
quelle
... mit Ausnahme des __main__Moduls, in dem sich __file__ möglicherweise ein relativer Pfad befindet.
0xC0000022L
5

Mithilfe der von @kindall bereitgestellten Guido-Mail können wir den Standardimportprozess so verstehen, dass versucht wird, das Modul in jedem Mitglied von sys.pathund die Datei als Ergebnis dieser Suche zu finden (weitere Details in PyMOTW-Module und -Importe ). Wenn sich das Modul also in einem absoluten Pfad sys.pathbefindet, ist das Ergebnis absolut, aber wenn es sich in einem relativen Pfad befindet, ist sys.pathdas Ergebnis relativ.

Jetzt site.pysorgt die Startdatei dafür, dass nur der absolute Pfad sys.pathmit Ausnahme des Anfangs ''eingegeben wird. Wenn Sie ihn also nicht auf andere Weise als durch Festlegen des PYTHONPATH ändern (dessen Pfad vor dem Präfix ebenfalls absolut gemacht wird sys.path), erhalten Sie immer einen absoluten Pfad, aber wenn auf das Modul über das aktuelle Verzeichnis zugegriffen wird.

Wenn Sie sys.path auf witzige Weise austricksen, können Sie alles bekommen.

Als Beispiel , wenn Sie einen Probe - Modul haben foo.pyin /tmp/mit dem Code:

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

Wenn Sie in / tmp gehen, erhalten Sie:

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

Wenn Sie in in /home/user, wenn Sie /tmpIhre hinzufügen, erhalten PYTHONPATHSie:

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

Selbst wenn Sie hinzufügen ../../tmp, wird es normalisiert und das Ergebnis ist das gleiche.

Aber wenn Sie anstatt PYTHONPATHdirekt einen lustigen Pfad zu verwenden, erhalten Sie ein Ergebnis, das so lustig ist wie die Ursache.

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

Guido erklärt im oben zitierten Thread, warum Python nicht versucht, alle Einträge in absolute Pfade umzuwandeln:

Wir möchten nicht bei jedem Import getpwd () aufrufen müssen. getpwd () ist relativ langsam und kann manchmal sofort fehlschlagen.

Ihr Pfad wird also so verwendet, wie er ist .

marcz
quelle