Caminhos relativos em Python

245

Estou criando um script auxiliar simples para o trabalho que copiará alguns arquivos de modelo em nossa base de código para o diretório atual. No entanto, não tenho o caminho absoluto para o diretório em que os modelos estão armazenados. Eu tenho um caminho relativo do script, mas quando eu chamo o script, ele trata isso como um caminho relativo ao diretório de trabalho atual. Existe uma maneira de especificar que esse URL relativo seja do local do script?

baudtack
fonte

Respostas:

324

No arquivo que possui o script, você deseja fazer algo assim:

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

Isso fornecerá o caminho absoluto para o arquivo que você está procurando. Observe que, se você estiver usando o setuptools, provavelmente deverá usar a API de recursos do pacote .

ATUALIZAÇÃO : estou respondendo a um comentário aqui para colar um exemplo de código. :-)

Estou correto ao pensar que __file__nem sempre está disponível (por exemplo, quando você executa o arquivo diretamente em vez de importá-lo)?

Suponho que você queira dizer o __main__script quando menciona a execução direta do arquivo. Nesse caso, não parece ser o caso no meu sistema (python 2.5.1 no 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

No entanto, eu sei que existem algumas peculiaridades __file__nas extensões C. Por exemplo, eu posso fazer isso no meu 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'

No entanto, isso gera uma exceção na minha máquina Windows.

Jason Baker
fonte
1
Estou correto ao pensar que o arquivo nem sempre está disponível (por exemplo, quando você executa o arquivo diretamente em vez de importá-lo)?
Stephen Edmonds
@ Stephen Edmonds Estou usando um arquivo que eu corro, em vez de importar, e funciona muito bem.
baudtack
22
Observe que você deve usar os.path.join em todos os lugares para portabilidade:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
ford,
22
os.path.dirname(__file__)pode dar uma cadeia vazia, usar os.path.dirname(os.path.abspath(__file__))vez
Dmitry Trofimov
14
É uma coisa pequena, mas POR FAVOR, não use dir como um nome de variável, pois é um builtin.
David
63

você precisa os.path.realpath(a amostra abaixo adiciona o diretório pai ao seu caminho)

import sys,os
sys.path.append(os.path.realpath('..'))
user989762
fonte
2
os.path.dirname(__file__)me deu uma corda vazia. Isso funcionou perfeitamente.
Darragh Enright
3
Isso parece fornecer ao pai do diretório a partir do qual o script é executado, não a localização do script.
Coquelicot
10
os.path.realpath('..')fornece o diretório pai do diretório de trabalho atual . Isso geralmente não é o que você deseja.
Martijn Pieters
1
@DarraghEnright: isso acontece apenas em um ambiente de empacotamento Python-script-to-exe. Essa é uma das raras exceções em que contar com o diretório de trabalho atual seria a alternativa.
Martijn Pieters
52

Como mencionado na resposta aceita

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

Eu só quero adicionar isso

a última string não pode começar com a barra invertida; na verdade, nenhuma string deve incluir uma barra invertida

Deve ser algo como

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

A resposta aceita pode ser enganosa em alguns casos, consulte este link para obter detalhes

Ahmed
fonte
4
Sim, o uso os.path.joiné melhor porque ele os une ao separador específico do SO.
Farshid T
'/relative/path...'não é um caminho relativo. Isso é intencional?
23418 steveire
Agora, essa resposta está desatualizada, pois a resposta principal foi editada para usar o caminho relativo adequado os.path.join(). O que resta é a preferência de usar cadeias separadas para cada elemento do caminho em vez de codificar o separador de caminhos.
Martijn Pieters
@MartijnPieters Sim, a resposta principal foi editada para corresponder a isso em parte, mas as strings separadas não são uma preferência - separar as picadas dessa maneira o torna independente do sistema operacional.
precisa saber é o seguinte
26

Agora é 2018, e o Python já evoluiu __future__há muito tempo. Assim como sobre usando o incrível pathlibque vem com o Python 3.4 para realizar a tarefa em vez de lutar com os, os.path, glob, shutil, etc.

Portanto, temos três caminhos aqui (possivelmente duplicados):

  • mod_path: qual é o caminho do script auxiliar simples
  • src_path: que contém alguns arquivos de modelo aguardando para serem copiados.
  • cwd: diretório atual , o destino desses arquivos de modelo.

e o problema é: não temos o caminho completo src_path, apenas sabemos que é um caminho relativo para o mod_path.

Agora vamos resolver isso com o incrível 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()

No futuro, é simples assim. : D


Além disso, podemos selecionar e verificar e copiar / mover esses arquivos de modelo com 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
fonte
14

Considere o meu 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
fonte
Quando executo isso no Windows, recebo um erro: FileNotFoundError: [Erro 2] Não existe esse arquivo ou diretório: '<caminho>' onde <caminho> possui os segmentos de caminho corretos, mas usa \\ para separadores.
lonstar
11

Consulte sys.path Como inicializado na inicialização do programa, o primeiro item desta lista, caminho [0], é o diretório que contém o script usado para chamar o interpretador Python.

Use esse caminho como a pasta raiz da qual você aplica o caminho relativo

>>> 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
fonte
3
Isso não é necessariamente verdade. Normalmente, sys.path [0] é uma string ou um ponto vazio, que é um caminho relativo para o diretório atual. Se você deseja o diretório atual, use os.getcwd.
276 Jason Baker
O pôster original comentou que o diretório de trabalho atual é o local errado para basear o caminho relativo. Você está correto ao dizer que sys.path [0] nem sempre é válido.
Tom Leys
Não, sys.path[0]nem sempre é definido como o diretório pai. O código Python pode ser invocado com -cou -matravés de um intérprete incorporado, quando o ponto sys.path[0]é definido como algo completamente diferente.
Martijn Pieters
6

Ao invés de usar

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

como na resposta aceita, seria mais 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__ retornará o arquivo do qual o módulo foi carregado, se foi carregado de um arquivo; portanto, se o arquivo com o script for chamado de outro lugar, o diretório retornado não estará correto.

Essas respostas fornecem mais detalhes: https://stackoverflow.com/a/31867043/5542253 e https://stackoverflow.com/a/50502/5542253

risonho
fonte
5
inspect.stack()é uma função cara de chamar. Ele recupera informações para todos os quadros de pilha, que você descarta e obtém apenas o primeiro. Basicamente, chama inspect.getfile()o objeto do módulo, que apenas retorna module.__file__. Você é muito melhor apenas usando __file__.
Martijn Pieters
4

Olá, primeiro de tudo, você deve entender as funções os.path.abspath (path) e os.path.relpath (path)

Em resumo, os.path.abspath (path) faz um caminho relativo para o caminho absoluto . E se o caminho fornecido for um caminho absoluto, a função retornará o mesmo caminho.

da mesma forma os.path.relpath (path) faz um caminho absoluto para o caminho relativo . E se o caminho fornecido for um caminho relativo, a função retornará o mesmo caminho.

O exemplo abaixo pode permitir que você entenda o conceito acima corretamente :

suponha que eu tenho um arquivo input_file_list.txt que contém a lista de arquivos de entrada a serem processados ​​pelo meu script python.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Se você ver a estrutura da pasta acima, o arquivo input_file_list.txt está presente na pasta Copyofconc e os arquivos a serem processados ​​pelo script python estão presentes na pasta conc

Mas o conteúdo do arquivo input_file_list.txt é como mostrado abaixo:

.. \ conc \ input1.dic

.. \ conc \ input2.dic

E o meu script python está presente no D: drive.

E o caminho relativo fornecido no arquivo input_file_list.txt é relativo ao caminho do arquivo input_file_list.txt .

Portanto, quando o script python executar o diretório de trabalho atual (use os.getcwd () para obter o caminho)

Como meu caminho relativo é relativo a input_file_list.txt , que é "D: \ Copyofconc" , tenho que alterar o diretório de trabalho atual para "D: \ Copyofconc" .

Então, eu tenho que usar os.chdir ('D: \ Copyofconc') , para que o diretório de trabalho atual seja "D: \ Copyofconc" .

Agora, para obter os arquivos input1.dic e input2.dic , lerei as linhas ".. \ conc \ input1.dic" e depois usarei o comando

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (para alterar o caminho relativo para o caminho absoluto. Aqui como diretório de trabalho atual é "D: \ Copyofconc", o arquivo ". \ conc \ input1. dic "deve ser acessado em relação a" D: \ Copyofconc ")

portanto, input1_path deve ser "D: \ conc \ input1.dic"

Chandrajyoti Das
fonte
4

Este código retornará o caminho absoluto para o script principal.

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

Isso funcionará mesmo em um módulo.

BookOwl
fonte
Em vez de reimportar, você usaria sys.modules['__main__'].__file__.
Martijn Pieters
3

Uma alternativa que funciona para mim:

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

O que funcionou para mim está usando sys.path.insert. Então eu especifiquei o diretório que eu precisava ir. Por exemplo, eu só precisava subir um diretório.

import sys
sys.path.insert(0, '../')
Gato branco
fonte
1
Isso depende do diretório de trabalho atual, que pode ser radicalmente diferente do que você realmente deseja.
Martijn Pieters
-2

Não tenho certeza se isso se aplica a algumas das versões mais antigas, mas acredito que o Python 3.3 tenha suporte nativo ao caminho relativo.

Por exemplo, o código a seguir deve criar um arquivo de texto na mesma pasta que o script python:

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

(observe que não deve haver uma barra invertida ou direta no início se for um caminho relativo)

Samy Bencherif
fonte
certo, então isso funcionará no CWD, que não é o que o OP solicita. O desejo de trabalhar a partir do local dos scripts.
Samy Bencherif