Rutas relativas en Python

245

Estoy creando un script de ayuda simple para el trabajo que copiará un par de archivos de plantilla en nuestra base de código al directorio actual. Sin embargo, no tengo la ruta absoluta al directorio donde se almacenan las plantillas. Tengo una ruta relativa desde el script pero cuando llamo al script lo trata como una ruta relativa al directorio de trabajo actual. ¿Hay alguna manera de especificar que esta url relativa es de la ubicación del script?

baudtack
fuente

Respuestas:

325

En el archivo que tiene el script, desea hacer algo como esto:

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

Esto le dará la ruta absoluta al archivo que está buscando. Tenga en cuenta que si está usando setuptools, probablemente debería usar su API de recursos de paquete en su lugar.

ACTUALIZACIÓN : estoy respondiendo a un comentario aquí para poder pegar una muestra de código. :-)

¿Estoy en lo cierto al pensar que __file__no siempre está disponible (por ejemplo, cuando ejecuta el archivo directamente en lugar de importarlo)?

Supongo que te refieres al __main__script cuando mencionas ejecutar el archivo directamente. Si es así, ese no parece ser el caso en mi sistema (python 2.5.1 en OS X 10.5.7):

#foo.py
import os
print os.getcwd()
print __file__

#in the interactive interpreter
>>> import foo
/Users/jason
foo.py

#and finally, at the shell:
~ % python foo.py
/Users/jason
foo.py

Sin embargo, sé que hay algunas peculiaridades con las __file__extensiones C. Por ejemplo, puedo hacer esto en mi Mac:

>>> import collections #note that collections is a C extension in Python 2.5
>>> collections.__file__
'/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-
dynload/collections.so'

Sin embargo, esto genera una excepción en mi máquina Windows.

Jason Baker
fuente
1
¿Estoy en lo cierto al pensar que el archivo no siempre está disponible (por ejemplo, cuando ejecuta el archivo directamente en lugar de importarlo)?
Stephen Edmonds
@Stephen Edmonds Estoy usando un archivo que ejecuto, en lugar de importar, y funciona muy bien.
baudtack
22
Tenga en cuenta que debe usar os.path.join en todas partes para la portabilidad:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
Ford
22
os.path.dirname(__file__)puede dar una cadena vacía, usar os.path.dirname(os.path.abspath(__file__))en su lugar
Dmitry Trofimov
14
Es una cosa menor, pero POR FAVOR no use dir como un nombre de variable ya que es un incorporado.
David
63

que necesita os.path.realpath(el ejemplo a continuación agrega el directorio principal a su ruta)

import sys,os
sys.path.append(os.path.realpath('..'))
usuario989762
fuente
2
os.path.dirname(__file__)me dio una cuerda vacía. Esto funcionó perfectamente.
Darragh Enright
3
Esto parece dar al padre del directorio desde el que se ejecuta el script, no de la ubicación del script.
Coquelicot
10
os.path.realpath('..')le da el directorio padre del directorio de trabajo actual . Eso generalmente no es lo que quieres.
Martijn Pieters
1
@DarraghEnright: Eso solo sucede en un entorno de empaquetado Python-script-to-exe. Esa es una de las raras excepciones donde confiar en el directorio de trabajo actual sería la alternativa.
Martijn Pieters
52

Como se menciona en la respuesta aceptada

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, '/relative/path/to/file/you/want')

Solo quiero agregar eso

la última cadena no puede comenzar con la barra invertida, de hecho, ninguna cadena debe incluir una barra invertida

Debería ser algo como

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'relative','path','to','file','you','want')

La respuesta aceptada puede ser engañosa en algunos casos, consulte este enlace para obtener más detalles.

Ahmed
fuente
44
Sí, usar os.path.joines mejor porque los une con el separador específico del sistema operativo.
Farshid T
'/relative/path...'No es un camino relativo. ¿Es eso intencional?
steveire
Esta respuesta ahora está desactualizada, ya que la respuesta superior se ha editado para usar una ruta relativa adecuada os.path.join(). Lo que queda es la preferencia de usar cadenas separadas para cada elemento de ruta en lugar de codificar el separador de ruta.
Martijn Pieters
@MartijnPieters Sí, la respuesta principal se ha editado para que coincida con esto en parte, pero las cadenas separadas no son una preferencia: separar las picaduras de esta manera lo hace independiente del sistema operativo.
jshrimp29
26

Ahora es 2018, y Python ya ha evolucionado __future__hace mucho tiempo. Entonces, ¿cómo sobre el uso de la sorprendente pathlibviene con Python 3.4 para realizar la tarea en lugar de luchar con os, os.path,glob , shutil, etc.

Entonces tenemos 3 caminos aquí (posiblemente duplicados):

  • mod_path: cuál es la ruta del script de ayuda simple
  • src_path: que contiene un par de archivos de plantilla que esperan ser copiados.
  • cwd: directorio actual , el destino de esos archivos de plantilla.

y el problema es: no tenemos la ruta completa de src_path, solo sabemos que es la ruta relativa a lamod_path .

Ahora solucionemos esto con lo sorprendente pathlib:

# Hope you don't be imprisoned by legacy Python code :)
from pathlib import Path

# `cwd`: current directory is straightforward
cwd = Path.cwd()

# `mod_path`: According to the accepted answer and combine with future power
# if we are in the `helper_script.py`
mod_path = Path(__file__).parent
# OR if we are `import helper_script`
mod_path = Path(helper_script.__file__).parent

# `src_path`: with the future power, it's just so straightforward
relative_path_1 = 'same/parent/with/helper/script/'
relative_path_2 = '../../or/any/level/up/'
src_path_1 = (mod_path / relative_path_1).resolve()
src_path_2 = (mod_path / relative_path_2).resolve()

En el futuro, es así de simple. :RE


Además, podemos seleccionar y verificar y copiar / mover esos archivos de plantilla con pathlib:

if src_path != cwd:
    # When we have different types of files in the `src_path`
    for template_path in src_path.glob('*.ini'):
        fname = template_path.name
        target = cwd / fname
        if not target.exists():
            # This is the COPY action
            with target.open(mode='wb') as fd:
                fd.write(template_path.read_bytes())
            # If we want MOVE action, we could use:
            # template_path.replace(target)
YaOzI
fuente
14

Considera mi código:

import os


def readFile(filename):
    filehandle = open(filename)
    print filehandle.read()
    filehandle.close()



fileDir = os.path.dirname(os.path.realpath('__file__'))
print fileDir

#For accessing the file in the same folder
filename = "same.txt"
readFile(filename)

#For accessing the file in a folder contained in the current folder
filename = os.path.join(fileDir, 'Folder1.1/same.txt')
readFile(filename)

#For accessing the file in the parent folder of the current folder
filename = os.path.join(fileDir, '../same.txt')
readFile(filename)

#For accessing the file inside a sibling folder.
filename = os.path.join(fileDir, '../Folder2/same.txt')
filename = os.path.abspath(os.path.realpath(filename))
print filename
readFile(filename)
Fahad Haleem
fuente
Cuando ejecuto esto en Windows, recibo un error: FileNotFoundError: [Errno 2] No existe dicho archivo o directorio: '<path>' donde <path> tiene los segmentos de ruta correctos pero usa \\ para los separadores.
lonstar
11

Ver sys.path Como se inicializó al iniciar el programa, el primer elemento de esta lista, ruta [0], es el directorio que contiene el script que se utilizó para invocar al intérprete de Python.

Use esta ruta como la carpeta raíz desde la que aplica su ruta relativa

>>> import sys
>>> import os.path
>>> sys.path[0]
'C:\\Python25\\Lib\\idlelib'
>>> os.path.relpath(sys.path[0], "path_to_libs") # if you have python 2.6
>>> os.path.join(sys.path[0], "path_to_libs")
'C:\\Python25\\Lib\\idlelib\\path_to_libs'
Tom Leys
fuente
3
Eso no es necesariamente cierto. Por lo general, sys.path [0] es una cadena vacía o un punto, que es una ruta relativa al directorio actual. Si desea el directorio actual, use os.getcwd.
Jason Baker
El póster original comentaba que el directorio de trabajo actual es el lugar equivocado para basar la ruta relativa. Tiene razón al decir que sys.path [0] no siempre es válido.
Tom Leys
No, sys.path[0]no siempre se establece en el directorio principal. El código de Python se puede invocar con -co a -mtravés de un intérprete incrustado, en cuyo punto sys.path[0]se establece algo completamente diferente.
Martijn Pieters
6

En lugar de usar

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

como en la respuesta aceptada, sería más robusto usar:

import inspect
import os
dirname = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

porque usar __file__ devolverá el archivo desde el que se cargó el módulo, si se cargó desde un archivo, por lo que si el archivo con el script se llama desde otro lugar, el directorio devuelto no será correcto.

Estas respuestas dan más detalles: https://stackoverflow.com/a/31867043/5542253 y https://stackoverflow.com/a/50502/5542253

sonriente
fuente
55
inspect.stack()Es una función costosa para llamar. Recupera información para todos los marcos de pila, que luego descartas y solo obtienes el superior. Básicamente llama inspect.getfile()al objeto del módulo, que simplemente regresa module.__file__. Eres mucho mejor de solo usarlo __file__.
Martijn Pieters
4

Hola, en primer lugar, debe comprender las funciones os.path.abspath (ruta) y os.path.relpath (ruta)

En resumen, os.path.abspath (ruta) crea una ruta relativa a la ruta absoluta . Y si la ruta proporcionada es en sí misma una ruta absoluta, entonces la función devuelve la misma ruta.

Del mismo modo os.path.relpath (ruta de acceso) realiza una ruta absoluta a ruta relativa . Y si la ruta proporcionada es en sí misma una ruta relativa, la función devuelve la misma ruta.

El siguiente ejemplo puede permitirle comprender el concepto anterior correctamente :

supongamos que tengo un archivo input_file_list.txt que contiene una lista de archivos de entrada para ser procesados ​​por mi script de Python.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Si ve la estructura de carpetas anterior, input_file_list.txt está presente en la carpeta Copyofconc y los archivos que procesará el script de Python están presentes en la carpeta conc

Pero el contenido del archivo input_file_list.txt es el que se muestra a continuación:

.. \ conc \ input1.dic

.. \ conc \ input2.dic

Y mi script de Python está presente en D: drive.

Y la ruta relativa proporcionada en el archivo input_file_list.txt es relativa a la ruta del archivo input_file_list.txt .

Entonces, cuando el script python ejecutará el directorio de trabajo actual (use os.getcwd () para obtener la ruta)

Como mi ruta relativa es relativa a input_file_list.txt , es decir "D: \ Copyofconc" , tengo que cambiar el directorio de trabajo actual a "D: \ Copyofconc" .

Así que tengo que usar os.chdir ('D: \ Copyofconc') , por lo que el directorio de trabajo actual será "D: \ Copyofconc" .

Ahora para obtener los archivos input1.dic y input2.dic , leeré las líneas ".. \ conc \ input1.dic" y luego usaré el comando

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (para cambiar la ruta relativa a la ruta absoluta. Aquí, como directorio de trabajo actual es "D: \ Copyofconc", el archivo ". \ conc \ input1. Dic "se accederá en relación con" D: \ Copyofconc ")

entonces input1_path será "D: \ conc \ input1.dic"

Chandrajyoti Das
fuente
4

Este código devolverá la ruta absoluta al script principal.

import os
def whereAmI():
    return os.path.dirname(os.path.realpath(__import__("__main__").__file__))

Esto funcionará incluso en un módulo.

BookOwl
fuente
En lugar de volver a importar, lo usarías sys.modules['__main__'].__file__.
Martijn Pieters
3

Una alternativa que funciona para mí:

this_dir = os.path.dirname(__file__) 
filename = os.path.realpath("{0}/relative/file.path".format(this_dir))
J0hnG4lt
fuente
0

Lo que funcionó para mí está usando sys.path.insert. Luego especifiqué el directorio que necesitaba ir. Por ejemplo, solo necesitaba subir un directorio.

import sys
sys.path.insert(0, '../')
Gato blanco
fuente
1
Esto se basa en el directorio de trabajo actual, que podría ser radicalmente diferente de lo que realmente desea.
Martijn Pieters
-2

No estoy seguro de si esto se aplica a algunas de las versiones anteriores, pero creo que Python 3.3 tiene soporte de ruta relativa nativa.

Por ejemplo, el siguiente código debería crear un archivo de texto en la misma carpeta que el script python:

open("text_file_name.txt", "w+t")

(tenga en cuenta que no debería haber una barra invertida o una barra invertida al principio si se trata de una ruta relativa)

Samy Bencherif
fuente
bien, entonces esto funcionará desde el CWD, que no es lo que pide el OP. El deseo de trabajar desde la ubicación de los scripts.
Samy Bencherif