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?


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.


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

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.


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)

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)

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'

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


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"


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.


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

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, '../')

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)


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.