Como se referir a caminhos relativos de recursos ao trabalhar com um repositório de código

187

Estamos trabalhando com um repositório de código implantado no Windows e Linux - às vezes em diretórios diferentes. Como um dos módulos dentro do projeto se refere a um dos recursos não-Python do projeto (arquivos CSV, etc.)?

Se fizermos algo como:

thefile=open('test.csv')

ou:

thefile=open('../somedirectory/test.csv')

Ele funcionará apenas quando o script for executado em um diretório específico ou em um subconjunto dos diretórios.

O que eu gostaria de fazer é algo como:

path=getBasePathOfProject()+'/somedirectory/test.csv'
thefile=open(path)

É possível?

olamundo
fonte

Respostas:

255

Tente usar um nome de arquivo relativo ao caminho dos arquivos atuais. Exemplo para './my_file':

fn = os.path.join(os.path.dirname(__file__), 'my_file')

No Python 3.4 ou superior, você também pode usar o pathlib :

fn = pathlib.Path(__file__).parent / 'my_file'
c089
fonte
3
Eu acho que essa solução só funcionará se o recurso estiver no mesmo diretório do arquivo python ou em um subdiretório. Como você resolvê-lo quando você tem a seguinte estrutura de árvore: / Project_Root_dir / python_files_dir / Alguns mais subdirs aqui py_file.py / recursos / alguns SUBDIRS aqui resource_file.csv
olamundo
1
Desculpe, a árvore de arquivos ficou distorcida nessa última mensagem ... segunda tentativa: você tem seu arquivo em /Project_Root_dir/python_files_dir/some_subdirs/py_file.py e seu arquivo de recurso em /Project_Root_dir/resources/some_subdirs/resource_file.csv
olamundo 14/08/09
28
Você deve conseguir acessar o diretório pai usando join (foo, '..'). Assim, a partir / root / python_files / módulo / myfile, uso os.path.join (os.path.dirname ( __file__), '.. '' ..', 'recursos')
c089
7
os.pardiré um pouco melhor que '..', embora os dois sejam equivalentes no POSIX e no Windows.
Davidchambers
4
@cedbeu: É equivalente em todos os sistemas que já encontrei e acho que todos os sistemas python funcionam hoje (por favor, corrija-me se estiver errado aqui). No entanto, se você espera que o python seja portado para um sistema usando um separador de caminho diferente no futuro e deseja que seu código esteja pronto para isso, o os.pardir será mais portátil. Eu diria que todo programador, mesmo quem nunca leu nenhum python, sabe o significado de "..", enquanto "os.pardir" é um nível de indireção que seria necessário procurar na documentação tão pessoalmente. d atenha-se a "..".
C089 24/03
40

Se você estiver usando ferramentas de configuração ou distribuir (uma instalação setup.py), a maneira "correta" de acessar esses recursos empacotados parece estar usando package_resources.

No seu caso, o exemplo seria

import pkg_resources
my_data = pkg_resources.resource_string(__name__, "foo.dat")

O que, é claro, lê o recurso e os dados binários lidos seriam o valor de my_data

Se você só precisa do nome do arquivo, também pode usar

resource_filename(package_or_requirement, resource_name)

Exemplo:

resource_filename("MyPackage","foo.dat")

A vantagem é que é garantido que funcione, mesmo que seja uma distribuição de arquivos como um ovo.

Consulte http://packages.python.org/distribute/pkg_resources.html#resourcemanager-api

Sharoon Thomas
fonte
3
Sei que essa é uma resposta antiga, minha maneira preferida é (/ talvez?) Usar pkg_resources, mas com o desaparecimento de ovos compactados, há algum dano em usar __file__como nos bons velhos tempos?
precisa saber é o seguinte
1
Essa é uma abordagem sólida. Mesmo que a convenção ovo está indo embora, setuptools não é e muitos estão ainda a instalação de deps contra repos git onde o ovo é construído em tempo de execução
deepelement
18

No Python, os caminhos são relativos ao diretório de trabalho atual , que na maioria dos casos é o diretório no qual você executa seu programa. O diretório de trabalho atual provavelmente não é o mesmo que o diretório do arquivo de módulo, portanto, usar um caminho relativo ao arquivo de módulo atual é sempre uma má escolha.

Usar caminho absoluto deve ser a melhor solução:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')
sem céu
fonte
15

Costumo usar algo semelhante a este:

import os
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))

# if you have more paths to set, you might want to shorten this as
here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
DATA_DIR = here('datadir') 

pathjoin = os.path.join
# ...
# later in script
for fn in os.listdir(DATA_DIR):
    f = open(pathjoin(DATA_DIR, fn))
    # ...

A variável

__file__

mantém o nome do arquivo do script no qual você escreve esse código, para que você possa criar caminhos em relação ao script, mas ainda gravados com caminhos absolutos. Funciona muito bem por vários motivos:

  • caminho é absoluto, mas ainda relativo
  • o projeto ainda pode ser implantado em um contêiner relativo

Mas você precisa observar a compatibilidade da plataforma - o os.pathsep do Windows é diferente do UNIX.

user137673
fonte
4
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

Você também tenta normalizar seu cwduso os.path.abspath(os.getcwd()). Mais informações aqui .

gavoja
fonte
3
muito poucos casos de uso onde a cwdé o caminho de um módulo, embora
cedbeu
não funciona dentro de um pacote, apenas a partir do mesmo diretório (ou o diretório de trabalho) definido pelo script.
Alexandra
Isso não funcionará se o usuário executar o programa usando o caminho absoluto de um diretório diferente. por exemplo python3 /usr/someone/test.py
sgrpwr
2

Você pode usar a compilação na __file__variável Ele contém o caminho do arquivo atual. Eu implementaria getBaseOfProject em um módulo na raiz do seu projeto. Lá eu pegaria o caminho __file__e o retornaria. Esse método pode ser usado em qualquer lugar do seu projeto.

Achim
fonte
0

Fiquei perplexo aqui um pouco. Queria empacotar alguns arquivos de recursos em um arquivo wheel e acessá-los. Fiz a embalagem usando o arquivo manifest, mas a instalação do pip não a instalava, a menos que fosse um subdiretório. Esperando que essas cenas ajudem

├── cnn_client
   ├── image_preprocessor.py
   ├── __init__.py
   ├── resources
      ├── mscoco_complete_label_map.pbtxt
      ├── retinanet_complete_label_map.pbtxt
      └── retinanet_label_map.py
   ├── tf_client.py

MANIFEST.in

recursive-include cnn_client/resources *

Criou um weel usando o setup.py padrão. O pip instalou o arquivo da roda. Após a instalação, verifique se os recursos estão instalados. Eles são

ls /usr/local/lib/python2.7/dist-packages/cnn_client/resources

mscoco_complete_label_map.pbtxt
retinanet_complete_label_map.pbtxt 
 retinanet_label_map.py  

Em tfclient.py para acessar esses arquivos. de

templates_dir = os.path.join(os.path.dirname(__file__), 'resources')
 file_path = os.path.join(templates_dir, \
            'mscoco_complete_label_map.pbtxt')
        s = open(file_path, 'r').read()

E isso funciona.

Alex Punnen
fonte
-5

Passei muito tempo tentando descobrir a resposta para isso, mas finalmente consegui (e é realmente muito simples):

import sys
import os
sys.path.append(os.getcwd() + '/your/subfolder/of/choice')

# now import whatever other modules you want, both the standard ones,
# as the ones supplied in your subfolders

Isso anexará o caminho relativo da sua subpasta aos diretórios para o python procurar. É bem rápido e sujo, mas funciona como um encanto :)

Rutger Semp
fonte
6
Isso só funcionará se você estiver executando o programa Python no mesmo diretório que o arquivo .py em questão. E nesse caso, você poderia fazer de open('your/subfolder/of/choice')qualquer maneira.
Paul Fisher
4
e o OP mencionou que o código precisa funcionar no Windows e Linux. Isso não vai acontecer.
user183037