Como obtenho o caminho do arquivo executado atualmente no Python?

193

Pode parecer uma pergunta para iniciantes, mas não é. Algumas abordagens comuns não funcionam em todos os casos:

sys.argv [0]

Isso significa usar path = os.path.abspath(os.path.dirname(sys.argv[0])), mas isso não funciona se você estiver executando em outro script Python em outro diretório, e isso pode acontecer na vida real.

__Arquivo__

Isso significa usar path = os.path.abspath(os.path.dirname(__file__)), mas descobri que isso não funciona:

  • py2exenão tem um __file__atributo, mas há uma solução alternativa
  • Quando você executa a partir do IDLE com execute()nenhum __file__atributo
  • OS X 10.6 onde eu recebo NameError: global name '__file__' is not defined

Perguntas relacionadas com respostas incompletas:

Estou procurando uma solução genérica , que funcionaria em todos os casos de uso acima.

Atualizar

Aqui está o resultado de um testcase:

Saída de python a.py (no Windows)

a.py: __file__= a.py
a.py: os.getcwd()= C:\zzz

b.py: sys.argv[0]= a.py
b.py: __file__= a.py
b.py: os.getcwd()= C:\zzz

a.py

#! /usr/bin/env python
import os, sys

print "a.py: sys.argv[0]=", sys.argv[0]
print "a.py: __file__=", __file__
print "a.py: os.getcwd()=", os.getcwd()
print

execfile("subdir/b.py")

subdir / b.py

#! /usr/bin/env python
import os, sys

print "b.py: sys.argv[0]=", sys.argv[0]
print "b.py: __file__=", __file__
print "b.py: os.getcwd()=", os.getcwd()
print

árvore

C:.
|   a.py
\---subdir
        b.py
sorin
fonte

Respostas:

80

Você não pode determinar diretamente a localização do script principal que está sendo executado. Afinal, às vezes o script não veio de um arquivo. Por exemplo, poderia vir do intérprete interativo ou do código gerado dinamicamente, armazenado apenas na memória.

No entanto, você pode determinar com segurança a localização de um módulo, pois os módulos sempre são carregados de um arquivo. Se você criar um módulo com o código a seguir e colocá-lo no mesmo diretório que o script principal, o script principal poderá importar o módulo e usá-lo para se localizar.

some_path / module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

some_path / main.py:

import module_locator
my_path = module_locator.module_path()

Se você tiver vários scripts principais em diretórios diferentes, poderá precisar de mais de uma cópia do module_locator.

Obviamente, se o seu script principal for carregado por alguma outra ferramenta que não permita a importação de módulos co-localizados com o seu script, você estará sem sorte. Em casos como esse, as informações que você procura simplesmente não existem em nenhum lugar do seu programa. Sua melhor aposta seria registrar um bug com os autores da ferramenta.

Daniel Stutzbach
fonte
2
Menciono que no OS 10.6 eu NameError: global name '__file__' is not defineduso o arquivo e isso não está dentro do IDLE. Pense que isso __file__é definido apenas dentro dos módulos.
Sorin
1
@Sorin Sbarnea: Atualizei minha resposta sobre como contornar isso.
Daniel Stutzbach 13/04
2
Obrigado, mas, na verdade, o problema de falta __file__não tinha nada a ver com Unicode. Não sei por que __file__não está definido, mas estou procurando uma solução genérica que funcione em todos os casos.
22410
1
Desculpe, isso não é possível em todos os casos. Por exemplo, tento fazer isso no waf.googlecode.com de dentro de um arquivo wscript (python). Esses arquivos são executados, mas não são módulos e você não pode transformá-los em módulos (eles podem ser qualquer subdiretório da árvore de origem).
21410
1
Isso não lhe dará a localização some_path/module_locator.py?
Casebash
68

Primeiro, você precisa importar de inspecteos

from inspect import getsourcefile
from os.path import abspath

Em seguida, onde você quiser encontrar o arquivo de origem, basta usar

abspath(getsourcefile(lambda:0))
ArtOfWarfare
fonte
Melhor resposta. Obrigado.
Devan Williams
4
Ótima resposta. Obrigado. Também parece ser a resposta mais curta e mais portátil (executando em sistemas operacionais diferentes) e não encontra problemas como NameError: global name '__file__' is not defined(outra solução causou isso).
Edward
Outra possibilidade que é igualmente curta: lambda:_. Funcionou para mim - não tenho certeza de que sempre funcionará. lambda:0provavelmente corre mais rápido em uma quantidade incomensuravelmente pequena (ou talvez não tão pequena ... pode ser uma carga imediata ou algo ainda mais rápido para 0uma carga global _?) Debutável se é mais limpo ou mais fácil de ler ou ainda mais inteligente / obscuro.
ArtOfWarfare
Como em quase todas as propostas que tentei, isso apenas retorna o cwd para mim, não o arquivo de diretório que estou executando (depuração).
James
1
@ James - Esse código entra no arquivo que você está executando ... a execução não getsourcefile(lambda:0)terá sentido e retornará Nonese você tentar executá-lo em um prompt interativo (já lambdaque não estará em nenhum arquivo de origem.) Se você quiser saber de onde outra função ou objeto é proveniente de um ambiente interativo, talvez abspath(getsourcefile(thatFunctionOrObject))seja mais útil para você?
ArtOfWarfare 23/09
16

essa solução é robusta mesmo em executáveis

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))
José Crespo Barrios
fonte
2
Essa deve ser a resposta correta. Isso funciona mesmo entry_point: console_script, mas nenhuma das outras respostas.
Poli
15

Eu estava com um problema semelhante e acho que isso pode resolver o problema:

def module_path(local_function):
   ''' returns the module path without the use of __file__.  Requires a function defined
   locally in the module.
   from http://stackoverflow.com/questions/729583/getting-file-path-of-imported-module'''
   return os.path.abspath(inspect.getsourcefile(local_function))

Funciona para scripts regulares e inativos. Tudo o que posso dizer é experimentá-lo para os outros!

Meu uso típico:

from toolbox import module_path
def main():
   pass # Do stuff

global __modpath__
__modpath__ = module_path(main)

Agora eu uso __modpath__ em vez de __file__.

Garrett Berg
fonte
2
De acordo com o guia de estilo de codificação PEP8 , nunca se deve criar nomes com sublinhados duplos à esquerda e à direita - portanto, __modpath__deve ser renomeado. Você provavelmente também não precisa da globaldeclaração. Caso contrário, +1!
30512 martineau
4
Na verdade, você pode definir a função local diretamente na chamada para module_path(). ou seja, module_path(lambda _: None)que não depende de outros conteúdo do script é no.
martineau
@martineau: Aceitei sua sugestão lambda _: Nonee a usei por quase dois anos, mas agora descobri que podia condensá-la apenas lambda:0. Existe alguma razão específica para sugerir o formulário que você fez, com um argumento ignorado _, em vez de sem nenhum argumento? Existe algo superior no Noneprefixo com um espaço do que apenas 0? Ambos são igualmente enigmáticos, acho, apenas um com 8 caracteres e o outro com 14 caracteres.
ArtOfWarfare 26/03
@ArtOfWarfare: A diferença entre os dois é que lambda _:é uma função que recebe um argumento e lambda:que não aceita nenhum. Não importa, pois a função nunca é chamada. Da mesma forma, não importa qual valor de retorno é usado. Acho que escolhi Noneporque, na época, parecia indicar que era uma função do tipo "não faça nada, que nunca será chamada". O espaço à sua frente é opcional e, novamente, existe apenas para melhorar a legibilidade (sempre tentar seguir o PEP8 é um hábito).
26515 martineau
@martineau: É obviamente um abuso lambda, porém, usá-lo para algo que nunca foi feito. Se você seguisse o PEP8, acho que o conteúdo certo seria pass, não None, mas não é válido colocar uma declaração em a lambda, então você deve colocar algo com um valor. Há algumas coisas válidas 2 de caracteres que você poderia colocar em, mas eu acho que a única válidos único caractere coisas que você pode colocar em são 0-9 (ou um único nome de variável de caráter atribuído fora do lambda.) Eu acho que 0melhor indica o nada de 0- 9
ArtOfWarfare 26/03
6

A resposta curta é que não há maneira garantida de obter as informações desejadas , no entanto, existem heurísticas que funcionam quase sempre na prática. Você pode ver Como encontro a localização do executável em C? . Ele discute o problema do ponto de vista C, mas as soluções propostas são facilmente transcritas para o Python.

Dale Hagglund
fonte
Olá, minha bandeira foi recusado assim que eu agora começou uma discussão sobre meta: meta.stackoverflow.com/questions/277272/...
ArtOfWarfare
No entanto, já existem respostas simples de trabalho nesta página. Minha solução funciona bem: stackoverflow.com/a/33531619/3787376 .
Edward
5

Consulte minha resposta para a pergunta Importando módulos da pasta pai para obter informações relacionadas, incluindo por que minha resposta não usa a __file__variável não confiável . Essa solução simples deve ser compatível com diferentes sistemas operacionais como módulos ose inspectfazer parte do Python.

Primeiro, você precisa importar partes dos módulos inspecionar e os .

from inspect import getsourcefile
from os.path import abspath

Em seguida, use a seguinte linha em qualquer outro local necessário no seu código Python:

abspath(getsourcefile(lambda:0))

Como funciona:

Do módulo interno os(descrição abaixo), a abspathferramenta é importada.

Rotinas de SO para Mac, NT ou Posix, dependendo do sistema em que estamos.

Em seguida getsourcefile(descrição abaixo) é importada do módulo interno inspect.

Obtenha informações úteis de objetos Python ao vivo.

  • abspath(path) retorna a versão absoluta / completa de um caminho de arquivo
  • getsourcefile(lambda:0)de alguma maneira obtém o arquivo de origem interno do objeto da função lambda, então retorna '<pyshell#nn>'no shell do Python ou retorna o caminho do arquivo do código Python atualmente sendo executado.

A utilização abspathdo resultado de getsourcefile(lambda:0)deve garantir que o caminho do arquivo gerado seja o caminho completo do arquivo Python.
Esta solução explicada foi originalmente baseada no código da resposta em Como obtenho o caminho do arquivo executado atualmente no Python? .

Edward
fonte
Sim, eu apenas vim com a mesma solução ... muito melhor do que as respostas que apenas dizem que não podem ser feitas com segurança ... A menos que a pergunta não esteja perguntando o que eu acho que é ...
Grady Player
5

Você simplesmente chamou:

path = os.path.abspath(os.path.dirname(sys.argv[0]))

ao invés de:

path = os.path.dirname(os.path.abspath(sys.argv[0]))

abspath()fornece o caminho absoluto de sys.argv[0](o nome do arquivo em que seu código está) e dirname()retorna o caminho do diretório sem o nome do arquivo.

dgb
fonte
3

Isso deve funcionar de uma maneira multiplataforma (desde que você não esteja usando o intérprete ou algo assim):

import os, sys
non_symbolic=os.path.realpath(sys.argv[0])
program_filepath=os.path.join(sys.path[0], os.path.basename(non_symbolic))

sys.path[0]é o diretório em que o script de chamada está (o primeiro lugar em que ele procura módulos a serem usados ​​por esse script). Podemos tirar o nome do arquivo em si (no final, sys.argv[0]fiz o que fiz os.path.basename). os.path.joinapenas os junta de uma maneira multiplataforma. os.path.realpathapenas garante que, se obtivermos links simbólicos com nomes diferentes do script em si, ainda assim obtemos o nome real do script.

Eu não tenho um Mac; então, eu não testei isso em um. Por favor, deixe-me saber se funciona, como parece que deveria. Eu testei isso no Linux (Xubuntu) com Python 3.4. Observe que muitas soluções para esse problema não funcionam em Macs (já que ouvi dizer que __file__não está presente em Macs).

Observe que, se seu script for um link simbólico, ele fornecerá o caminho do arquivo ao qual ele vincula (e não o caminho do link simbólico).

Brōtsyorfuzthrāx
fonte
2

Você pode usar Pathno pathlibmódulo:

from pathlib import Path

# ...

Path(__file__)

Você pode usar call parentpara ir mais longe no caminho:

Path(__file__).parent
Gavriel Cohen
fonte
Esta resposta usa a __file__variável que pode não ser confiável (nem sempre é o caminho completo do arquivo, não funciona em todos os sistemas operacionais etc.), como os usuários do StackOverflow mencionaram frequentemente. Alterar a resposta para não incluí-la causará menos problemas e será mais compatível. Para obter mais informações, consulte stackoverflow.com/a/33532002/3787376 .
Edward
@ mrroot5 Ok, então exclua seu comentário, por favor.
precisa saber é o seguinte
1

Se o código vier de um arquivo, você poderá obter o nome completo

sys._getframe().f_code.co_filename

Você também pode recuperar o nome da função como f_code.co_name

Alex Cohn
fonte
0

Basta adicionar o seguinte:

from sys import *
path_to_current_file = sys.argv[0]
print(path_to_current_file)

Ou:

from sys import *
print(sys.argv[0])
H. Kamran
fonte
0

Minha solução é:

import os
print(os.path.dirname(os.path.abspath(__file__)))
ThienSuBS
fonte
-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))
Mohamed.Abdo
fonte
Isto não faz sentido. Primeiro, '__file__'não deve ser uma string; segundo, se você o fizer __file__, isso funcionaria apenas para o arquivo em que esta linha de código está, e não para o arquivo que é executado?
Andreas Storvik Strauman