¿El atributo __file__ del módulo es absoluto o relativo?

107

Me cuesta entender __file__. Por lo que tengo entendido, __file__devuelve la ruta absoluta desde la que se cargó el módulo.

Tengo problemas para producir esto: tengo abc.pyuna declaración con una print __file__, que se ejecuta desde /d/projects/ python abc.pydevoluciones abc.py. corriendo de /d/devoluciones projects/abc.py. ¿Alguna razón por la que?

goh
fuente
10
Esto es lo que Guido tiene que decir sobre esto: mail.python.org/pipermail/python-dev/2010-February/097461.html
poco el
Relevante: stackoverflow.com/q/9271464/1959808
Ioannis Filippidis

Respuestas:

98

De la documentación :

__file__es la ruta del archivo desde el cual se cargó el módulo, si se cargó desde un archivo. El __file__atributo no está presente para los módulos C que están vinculados estáticamente al intérprete; para los módulos de extensión cargados dinámicamente desde una biblioteca compartida, es el nombre de ruta del archivo de la biblioteca compartida.

Desde el hilo de la lista de correo vinculado por @kindall en un comentario a la pregunta:

No he intentado reproducir este ejemplo en particular, pero la razón es que no queremos tener que llamar a getpwd () en cada importación ni queremos tener algún tipo de variable en proceso para almacenar en caché el directorio actual. (getpwd () es relativamente lento y, a veces, puede fallar por completo, y tratar de almacenarlo en caché tiene cierto riesgo de equivocarse).

En su lugar, lo que hacemos es código en site.py que recorre los elementos de sys.path y los convierte en rutas absolutas. Sin embargo, este código se ejecuta antes de que se inserte "" al principio de sys.path, de modo que el valor inicial de sys.path sea "".

Para el resto de esto, considere sys.pathno incluir ''.

Entonces, si está fuera de la parte sys.pathque contiene el módulo, obtendrá una ruta absoluta . Si está dentro de la parte sys.pathque contiene el módulo, obtendrá una ruta relativa .

Si se carga un módulo en el directorio actual, y el directorio actual no es en sys.path, obtendrá una ruta absoluta.

Si carga un módulo en el directorio actual y el directorio actual está dentro sys.path, obtendrá una ruta relativa.

agf
fuente
también significa que si hay una ruta desde '' al módulo, se usaría una ruta relativa, si no se usaría una ruta absoluta, ya que el resto de sys.path son absolutas ..
goh
4
Si se carga un módulo en el directorio actual, y el directorio actual no es en sys.path, obtendrá una ruta absoluta. Si carga un módulo en el directorio actual y el directorio actual está dentro sys.path, obtendrá una ruta relativa.
agf
Recuerde, para este propósito, sys.pathno incluye ''.
agf
lo tengo, pero @agf, si uso python /foo/abc.py desde / home, supongo que la parte de sys.path que contiene el módulo es / home / foo y mi directorio actual es / home /, ¿por qué imprime el archivo me da una ruta relativa?
goh
55

__file__es absoluto desde Python 3.4 , excepto cuando se ejecuta un script directamente usando una ruta relativa:

Los __file__atributos del módulo (y los valores relacionados) ahora siempre deben contener rutas absolutas de forma predeterminada, con la única excepción de __main__.__file__cuando un script se ha ejecutado directamente usando una ruta relativa. (Contribuido por Brett Cannon en bpo-18416 .)

Sin embargo, no estoy seguro de si resuelve los enlaces simbólicos.

Ejemplo de pasar una ruta relativa:

$ python script.py
anatoly techtonik
fuente
1
Gracias. ¡Este es un hecho difícil de rastrear!
meawoppl
4
Esto no es cierto para Python 3.4.0 ( Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux). Y los enlaces simbólicos no se resuelven en mis pruebas.
Frozen Flame
@FrozenFlame, no dude en informar a bugs.python.org si 3.4.1 no lo soluciona.
anatoly techtonik
2
¿Es os.path.realpath(__file__)la forma correcta de resolver enlaces simbólicos?
kevinarpe
3
@kevinarpe, stackoverflow.com/questions/3220755/…
anatoly techtonik
16

Ejemplo simple tardío:

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

En Python-2. *, La segunda llamada determina incorrectamente el path.abspath(__file__)basado en el directorio actual:

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

Como señaló @techtonik, en Python 3.4+, esto funcionará bien ya que __file__devuelve una ruta absoluta.

SimplyKnownAsG
fuente
... excepto por el __main__módulo, donde __file__ puede haber una ruta relativa.
0xC0000022L
5

Con la ayuda del correo de Guido proporcionado por @kindall, podemos entender el proceso de importación estándar como tratar de encontrar el módulo en cada miembro de sys.pathy el archivo como resultado de esta búsqueda (más detalles en PyMOTW Modules and Imports .). Entonces, si el módulo está ubicado en una ruta absoluta, sys.pathel resultado es absoluto, pero si está ubicado en una ruta relativa, sys.pathel resultado es relativo.

Ahora el site.pyarchivo de inicio se encarga de entregar solo la ruta absoluta sys.path, excepto la inicial '', por lo que si no lo cambia por otros medios que no sean la configuración de PYTHONPATH (cuya ruta también se hace absoluta, antes de prefijar sys.path), obtendrá siempre un valor absoluto ruta, pero cuando se accede al módulo a través del directorio actual.

Ahora, si engaña a sys.path de una manera divertida, puede obtener cualquier cosa.

Como ejemplo, si tiene un módulo de muestra foo.pyen /tmp/el código:

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

Si entra / tmp obtiene:

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

Cuando /home/userestás adentro , si agregas /tmptu PYTHONPATHobtienes:

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

Incluso si agrega ../../tmp, se normalizará y el resultado será el mismo.

Pero si en lugar de usar PYTHONPATHusas directamente algún camino divertido obtienes un resultado tan divertido como la causa.

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

Guido explica en el hilo citado anteriormente, por qué Python no intenta transformar todas las entradas en rutas absolutas:

no queremos tener que llamar a getpwd () en cada importación .... getpwd () es relativamente lento y, a veces, puede fallar por completo,

Entonces tu camino se usa como está .

Marcz
fuente