Cómo abrir de manera confiable un archivo en el mismo directorio que un script de Python

Solía ​​abrir archivos que estaban en el mismo directorio que el script de Python que se ejecuta actualmente simplemente usando un comando como

open("Some file.txt", "r") 

Sin embargo, descubrí que cuando el script se ejecutaba en Windows haciendo doble clic en él, intentaría abrir el archivo desde el directorio incorrecto.

Desde entonces he usado un comando del formulario.

 open(os.path.join(sys.path[0], "Some file.txt"), "r") 

Cada vez que quería abrir un archivo. Esto funciona para mi uso particular, pero no estoy seguro de que sys.path[0] pueda fallar en algún otro caso de uso.

Entonces, mi pregunta es: ¿Cuál es la mejor y más confiable manera de abrir un archivo que se encuentra en el mismo directorio que el script de Python actualmente en ejecución?

Esto es lo que he podido averiguar hasta ahora:

  • os.getcwd() y os.path.abspath('') devuelven el “directorio de trabajo actual”, no el directorio del script.

  • os.path.dirname(sys.argv[0]) y os.path.dirname(__file__) devuelven la ruta utilizada para llamar al script, que puede ser relativa o incluso en blanco (si el script está en el cwd). Además, __file__ no existe cuando el script se ejecuta en IDLE o PythonWin.

  • sys.path[0] y os.path.abspath(os.path.dirname(sys.argv[0])) parecen devolver el directorio del script. No estoy seguro de si hay alguna diferencia entre estos dos.

Editar:

Acabo de darme cuenta de que lo que quiero hacer se describiría mejor como “abrir un archivo en el mismo directorio que el módulo que contiene”. En otras palabras, si importo un módulo que escribí que está en otro directorio, y ese módulo abre un archivo, quiero que busque el archivo en el directorio del módulo. No creo que nada de lo que he encontrado sea capaz de hacer eso …

Yo siempre uso:

 __location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) 

La llamada join() antepone al directorio de trabajo actual, pero la documentación dice que si alguna ruta es absoluta, todas las demás se omiten. Por lo tanto, getcwd() se descarta cuando dirname(__file__) devuelve una ruta absoluta.

Además, la llamada realpath resuelve los enlaces simbólicos si se encuentran. Esto evita problemas cuando se implementa con setuptools en sistemas Linux (los scripts están vinculados a /usr/bin/ , al menos en Debian).

Puede usar lo siguiente para abrir archivos en la misma carpeta:

 f = open(os.path.join(__location__, 'bundled-resource.jpg')); # ... 

¡Lo uso para agrupar recursos con varias aplicaciones Django tanto en Windows como en Linux y funciona como un encanto!

Para citar de la documentación de Python:

Como se inicializó al iniciar el progtwig, el primer elemento de esta lista, ruta [0], es el directorio que contiene el script que se usó para invocar al intérprete de Python. Si el directorio del script no está disponible (por ejemplo, si el intérprete se invoca interactivamente o si el script se lee desde la entrada estándar), la ruta [0] es la cadena vacía, que dirige a Python a buscar módulos en el directorio actual primero. Observe que el directorio de secuencias de comandos se inserta antes de las entradas insertadas como resultado de PYTHONPATH.

sys.path [0] es lo que estás buscando.

Ok esto es lo que hago

sys.argv es siempre lo que escribe en el terminal o usa como ruta de acceso al archivo cuando lo ejecuta con python.exe o pythonw.exe

Por ejemplo, puede ejecutar el archivo text.py de varias maneras, cada una le da una respuesta diferente, siempre le dan la ruta en la que se escribió Python.

  C:\Documents and Settings\Admin>python test.py sys.argv[0]: test.py C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py" sys.argv[0]: C:\Documents and Settings\Admin\test.py 

Ok, ahora que puedes obtener el nombre del archivo, gran cosa, ahora para obtener el directorio de la aplicación que puedes usar usa os.path, específicamente abspath y dirname

  import sys, os print os.path.dirname(os.path.abspath(sys.argv[0])) 

Eso dará salida a esto:

  C:\Documents and Settings\Admin\ 

siempre generará esto sin importar si escribe python test.py o python “C: \ Documents and Settings \ Admin \ test.py”

El problema con el uso de __archivo__ Considere estos dos archivos test.py

 import sys import os def paths(): print "__file__: %s" % __file__ print "sys.argv: %s" % sys.argv[0] a_f = os.path.abspath(__file__) a_s = os.path.abspath(sys.argv[0]) print "abs __file__: %s" % a_f print "abs sys.argv: %s" % a_s if __name__ == "__main__": paths() 

import_test.py

 import test import sys test.paths() print "--------" print __file__ print sys.argv[0] 

Salida de “python test.py”

 C:\Documents and Settings\Admin>python test.py __file__: test.py sys.argv: test.py abs __file__: C:\Documents and Settings\Admin\test.py abs sys.argv: C:\Documents and Settings\Admin\test.py 

Salida de “python test_import.py”

 C:\Documents and Settings\Admin>python test_import.py __file__: C:\Documents and Settings\Admin\test.pyc sys.argv: test_import.py abs __file__: C:\Documents and Settings\Admin\test.pyc abs sys.argv: C:\Documents and Settings\Admin\test_import.py -------- test_import.py test_import.py 

Entonces, como puede ver, el archivo siempre le da el archivo python desde el que se está ejecutando, mientras que sys.argv [0] siempre le da el archivo que ejecutó desde el intérprete. Dependiendo de sus necesidades, tendrá que elegir la que mejor se adapte a sus necesidades.

Lo haría de esta manera:

 from os.path import abspath, exists f_path = abspath("fooabar.txt") if exists(f_path): with open(f_path) as f: print f.read() 

El código anterior construye una ruta absoluta al archivo usando abspath y es equivalente a usar normpath(join(os.getcwd(), path)) [que es de pydocs]. Luego verifica si ese archivo existe realmente y luego usa un administrador de contexto para abrirlo, de modo que no tenga que recordar cerrar el identificador del archivo. En mi humilde opinión, hacerlo de esta manera te ahorrará mucho dolor a largo plazo.