Como abrir de forma confiável um arquivo no mesmo diretório que um script Python


157

Eu costumava abrir arquivos que estavam no mesmo diretório do script Python em execução no momento, simplesmente usando um comando como

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

No entanto, descobri que, quando o script era executado no Windows, clicando duas vezes nele, tentava abrir o arquivo do diretório errado.

Desde então, eu usei um comando do formulário

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

sempre que eu queria abrir um arquivo. Isso funciona para meu uso específico, mas não tenho certeza se sys.path[0]pode falhar em outro caso de uso.

Portanto, minha pergunta é: Qual é a melhor e mais confiável maneira de abrir um arquivo que está no mesmo diretório do script Python em execução no momento?

Aqui está o que eu consegui descobrir até agora:

  • os.getcwd()e os.path.abspath('')retorne o "diretório de trabalho atual", não o diretório de scripts.

  • os.path.dirname(sys.argv[0])e os.path.dirname(__file__)retorne o caminho usado para chamar o script, que pode ser relativo ou mesmo em branco (se o script estiver no cwd). Além disso, __file__não existe quando o script é executado no IDLE ou no PythonWin.

  • sys.path[0]e os.path.abspath(os.path.dirname(sys.argv[0]))parece retornar o diretório de scripts. Não tenho certeza se há alguma diferença entre esses dois.

Editar:

Acabei de perceber que o que eu quero fazer seria melhor descrito como "abra um arquivo no mesmo diretório que o módulo que o contém". Em outras palavras, se eu importar um módulo que escrevi que esteja em outro diretório e esse módulo abrir um arquivo, quero que ele procure o arquivo no diretório do módulo. Acho que nada que eu encontrei é capaz de fazer isso ...

Respostas:


199

Eu sempre uso:

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

A join()chamada precede o diretório de trabalho atual, mas a documentação diz que, se algum caminho for absoluto, todos os outros caminhos restantes serão descartados. Portanto, getcwd()é descartado quandodirname(__file__) retorna um caminho absoluto.

Além disso, a realpathchamada resolve links simbólicos, se houver algum. Isso evita problemas ao implementar com as ferramentas de instalação nos sistemas Linux (os scripts são vinculados a/usr/bin/ - pelo menos no Debian).

Você pode usar o seguinte para abrir arquivos na mesma pasta:

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

Eu uso isso para agrupar recursos com vários aplicativos Django no Windows e Linux e funciona como um encanto!


4
Se __file__não puder ser usado, use em sys.argv[0]vez de dirname(__file__). O resto deve funcionar como esperado. Eu gosto de usar __file__porque no código da biblioteca, sys.argv[0]talvez não aponte para o seu código, especialmente se importado por algum script de terceiros.
André Caron

1
O problema é que isso varia se o arquivo que está sendo executado for diretamente do interruptor ou se for importado. Veja minha resposta para as diferenças entre arquivo e sys.argv [0]
Zimm3r

Então, é correto dizer que a variação descrita na resposta do Zimm3r é tratada usando realpath( join( getcwd(), dirname(__file__) ))como descrito aqui?
pianoJames

44

Para citar a documentação do Python:

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. Se o diretório do script não estiver disponível (por exemplo, se o intérprete for chamado interativamente ou se o script for lido a partir da entrada padrão), o caminho [0] será a string vazia, que instrui o Python a pesquisar os módulos no diretório atual primeiro. Observe que o diretório do script é inserido antes das entradas inseridas como resultado do PYTHONPATH.

sys.path [0] é o que você está procurando.


10
E para o caminho completo do arquivo: os.path.join(sys.path[0], 'some file.txt'). Isso deve lidar com espaços e barras corretamente em todos os sistemas.
Jacktose

Esta resposta para a primeira pergunta, não a após a edição.
mcoolive

22

Ok, aqui está o que eu faço

sys.argv é sempre o que você digita no terminal ou usa como caminho de arquivo ao executá-lo com python.exe ou pythonw.exe

Por exemplo, você pode executar o arquivo text.py de várias maneiras, cada uma com uma resposta diferente, sempre com o caminho que o python foi digitado.

    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, então saiba que você pode obter o nome do arquivo, grande coisa. Agora, para obter o diretório do aplicativo, você pode usar o os.path, especificamente abspath e dirname

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

Isso produzirá o seguinte:

   C:\Documents and Settings\Admin\

ele sempre produzirá isso, não importa se você digita python test.py ou python "C: \ Documents and Settings \ Admin \ test.py"

O problema de usar __file__ Considere estes dois arquivos 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]

Saída 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

Saída 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

Então, como você pode ver, o arquivo fornece sempre o arquivo python do qual está sendo executado, enquanto sys.argv [0] fornece o arquivo que você executou sempre do intérprete. Dependendo das suas necessidades, você precisará escolher qual melhor se adequa às suas necessidades.


3
Essa é uma prova elaborada de que a implementação reflete a documentação. __file__é suposto que "sempre dar-lhe o caminho para o arquivo atual", e sys.argv[0]é suposto que "sempre dar o caminho do script que iniciou o processo". De qualquer forma, o uso __file__do script invocado sempre fornece resultados precisos.
André Caron

Se você tiver a referência __file__no nível superior do script, ele funcionará conforme o esperado.
Matthew Schinckel


-3

Eu faria assim:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

if exists(f_path):
    with open(f_path) as f:
        print f.read()

O código acima cria um caminho absoluto para o arquivo usando abspath e é equivalente a usar normpath(join(os.getcwd(), path))[isso é dos pydocs]. Em seguida, ele verifica se esse arquivo realmente existe e, em seguida, usa um gerenciador de contexto para abri-lo, para que você não precise se lembrar de fechar o identificador de arquivo. IMHO, fazê-lo dessa maneira, poupará muita dor a longo prazo.


Isso não responde à pergunta do pôster. O dln385 disse especificamente que os.path.abspathnão resolve caminhos para arquivos na mesma pasta que o script se o script não estiver no diretório atual.
André Caron

Ah! Eu assumi que o usuário estava executando esse script no mesmo diretório que o arquivo que eles desejavam ler, NÃO no diretório do módulo de algo em seu PYTHONPATH. Isso vai me ensinar a fazer suposições ...
dcolish

O abspath não funcionará, pois é impossível para o tempo de execução do python procurar no sistema de arquivos do SO usando uma função como esta.
Akshat Thakar
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.