Bagaimana cara mendapatkan lintasan file yang sedang dieksekusi saat ini dalam Python?

193

Ini mungkin tampak seperti pertanyaan pemula, tetapi tidak. Beberapa pendekatan umum tidak berfungsi dalam semua kasus:

sys.argv [0]

Ini berarti menggunakan path = os.path.abspath(os.path.dirname(sys.argv[0])), tetapi ini tidak berfungsi jika Anda menjalankan dari skrip Python lain di direktori lain, dan ini bisa terjadi dalam kehidupan nyata.

__mengajukan__

Ini berarti menggunakan path = os.path.abspath(os.path.dirname(__file__)), tetapi saya menemukan bahwa ini tidak berhasil:

  • py2exetidak memiliki __file__atribut, tetapi ada solusinya
  • Ketika Anda menjalankan dari IDLE dengan execute()tidak ada __file__atribut
  • OS X 10.6 di mana saya dapatkan NameError: global name '__file__' is not defined

Pertanyaan terkait dengan jawaban yang tidak lengkap:

Saya mencari solusi generik , yang akan bekerja di semua kasus penggunaan di atas.

Memperbarui

Ini adalah hasil dari testcase:

Output dari python a.py (di 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

pohon

C:.
|   a.py
\---subdir
        b.py
Sorin
sumber

Jawaban:

80

Anda tidak dapat secara langsung menentukan lokasi skrip utama yang dieksekusi. Lagi pula, terkadang skrip tidak berasal dari file sama sekali. Misalnya, itu bisa berasal dari interpreter interaktif atau kode yang dihasilkan secara dinamis yang hanya disimpan dalam memori.

Namun, Anda dapat menentukan lokasi modul dengan andal, karena modul selalu dimuat dari file. Jika Anda membuat modul dengan kode berikut dan meletakkannya di direktori yang sama dengan skrip utama Anda, maka skrip utama dapat mengimpor modul dan menggunakannya untuk mencari sendiri.

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

Jika Anda memiliki beberapa skrip utama di direktori yang berbeda, Anda mungkin memerlukan lebih dari satu salinan module_locator.

Tentu saja, jika skrip utama Anda dimuat oleh beberapa alat lain yang tidak memungkinkan Anda mengimpor modul yang berlokasi bersama dengan skrip Anda, maka Anda kurang beruntung. Dalam kasus seperti itu, informasi yang Anda cari tidak ada di program Anda. Taruhan terbaik Anda adalah mengajukan bug kepada pembuat alat.

Daniel Stutzbach
sumber
2
Saya menyebutkan bahwa pada OS 10.6 saya dapat NameError: global name '__file__' is not definedmenggunakan file dan ini bukan di dalam IDLE. Pikirkan yang __file__didefinisikan hanya di dalam modul.
Sorin
1
@ Korin Sbarnea: Saya memperbarui jawaban saya dengan cara saya menyiasatinya.
Daniel Stutzbach
2
Terima kasih, tetapi sebenarnya masalah dengan hilang __file__tidak ada hubungannya dengan Unicode. Saya tidak tahu mengapa __file__tidak didefinisikan tetapi saya mencari solusi generik ini akan bekerja pada semua kasus.
sorin
1
Maaf ini tidak mungkin dalam semua kasus. Misalnya saya mencoba melakukan ini di waf.googlecode.com dari dalam file wscript (python). File-file ini dieksekusi tetapi mereka bukan modul dan Anda tidak dapat membuatnya modul (mereka bisa berupa subdirektori dari pohon sumber).
Sorin
1
Bukankah itu memberi Anda lokasi some_path/module_locator.pybukan?
Casebash
68

Pertama, Anda perlu mengimpor dari inspectdanos

from inspect import getsourcefile
from os.path import abspath

Selanjutnya, di mana pun Anda ingin mencari file sumber dari yang baru saja Anda gunakan

abspath(getsourcefile(lambda:0))
ArtOfWarfare
sumber
Jawaban Terbaik. Terima kasih.
Devan Williams 4-15
4
Jawaban yang bagus Terima kasih. Tampaknya juga menjadi jawaban terpendek dan paling portabel (berjalan pada berbagai os) dan tidak mengalami masalah seperti NameError: global name '__file__' is not defined(solusi lain yang menyebabkan ini).
Edward
Kemungkinan lain yang sama-sama pendek: lambda:_. Itu bekerja untuk saya - tidak yakin itu akan selalu berhasil. lambda:0mungkin berjalan lebih cepat dengan jumlah yang sangat kecil (atau mungkin tidak terlalu kecil ... mungkin menjadi beban langsung atau sesuatu yang bahkan lebih cepat untuk 0vs beban global untuk _?) Debat apakah itu lebih bersih atau lebih mudah dibaca atau bahkan lebih pintar / tidak jelas.
ArtOfWarfare
Seperti hampir setiap proposal yang saya coba, ini hanya mengembalikan cwd untuk saya, bukan file direktori yang saya jalankan (debugging).
James
1
@James - Kode ini masuk dalam file yang Anda jalankan ... menjalankan getsourcefile(lambda:0)akan menjadi tidak berarti dan hanya kembali Nonejika Anda mencoba menjalankannya pada prompt interaktif (karena lambdatidak akan ada dalam file sumber apa pun.) Jika Anda ingin tahu dari mana fungsi atau objek lain berasal dari lingkungan yang interaktif, mungkin abspath(getsourcefile(thatFunctionOrObject))akan lebih membantu bagi Anda?
ArtOfWarfare
16

solusi ini sangat kuat bahkan di executables

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))
José Crespo Barrios
sumber
2
Ini seharusnya jawaban yang benar. Ini bekerja bahkan di entry_point: console_script, tetapi tidak ada jawaban lain.
Polv
15

Saya mengalami masalah yang sama, dan saya pikir ini mungkin menyelesaikan masalah:

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

Ini berfungsi untuk skrip biasa dan dalam keadaan diam. Yang bisa saya katakan adalah mencobanya untuk orang lain!

Penggunaan khas saya:

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

global __modpath__
__modpath__ = module_path(main)

Sekarang saya menggunakan __modpath__ bukan __file__.

Garrett Berg
sumber
2
Menurut panduan gaya pengkodean PEP8 , seseorang seharusnya tidak pernah membuat nama dengan garis bawah yang mengarah dan mengikuti ganda - jadi __modpath__harus diganti namanya. Anda juga mungkin tidak perlu globalpernyataan itu. Kalau tidak +1!
martineau
4
Sebenarnya Anda dapat mendefinisikan fungsi lokal tepat di panggilan ke module_path(). yaitu module_path(lambda _: None)yang tidak tergantung pada isi naskah yang lain.
martineau
@martineau: Saya menerima saran Anda lambda _: Nonedan telah menggunakannya selama hampir dua tahun terakhir, tetapi baru saja saya tahu saya bisa memadatkannya menjadi adil lambda:0. Apakah ada alasan khusus Anda menyarankan formulir yang Anda lakukan, dengan argumen diabaikan _, alih-alih tanpa argumen sama sekali? Apakah ada sesuatu yang superior tentang Noneawalan dengan spasi bukan hanya 0? Keduanya sama-sama samar, saya pikir, hanya satu yang panjangnya 8 karakter sementara yang lain panjangnya 14 karakter.
ArtOfWarfare
@ ArtOfWarfare: Perbedaan antara keduanya adalah bahwa itu lambda _:adalah fungsi yang mengambil satu argumen dan lambda:yang tidak mengambil. Tidak masalah karena fungsinya tidak pernah dipanggil. Demikian juga tidak masalah apa nilai pengembalian yang digunakan. Saya kira saya memilih Nonekarena pada saat itu sepertinya menunjukkan itu adalah fungsi yang tidak melakukan apa-apa, tidak pernah disebut lebih baik. Ruang di depannya adalah opsional dan sekali lagi hanya ada untuk meningkatkan keterbacaan (selalu berusaha untuk mengikuti PEP8 adalah membentuk kebiasaan).
martineau
@martineau: Jelas itu penyalahgunaan lambda, menggunakannya untuk sesuatu yang tidak pernah dimaksudkan untuk dilakukan. Jika Anda mengikuti PEP8, saya pikir konten yang tepat untuk itu adalah pass, tidak None, tapi itu tidak valid untuk memasukkan pernyataan di lambda, jadi Anda harus memasukkan sesuatu dengan nilai. Ada beberapa hal 2 karakter yang valid yang dapat Anda masukkan, tetapi saya pikir satu-satunya karakter karakter yang valid yang dapat Anda masukkan adalah 0-9 (atau nama variabel karakter tunggal yang ditetapkan di luar lambda.) Saya pikir yang 0terbaik menunjukkan ketiadaan 0- 9.
ArtOfWarfare
6

Jawaban singkatnya adalah tidak ada cara yang dijamin untuk mendapatkan informasi yang Anda inginkan , namun ada heuristik yang bekerja hampir selalu dalam praktik. Anda mungkin melihat Bagaimana cara menemukan lokasi executable di C? . Ini membahas masalah dari sudut pandang C, tetapi solusi yang diusulkan mudah ditranskripsi menjadi Python.

Dale Hagglund
sumber
Halo, bendera saya ditolak, jadi sekarang saya telah memulai diskusi tentang meta: meta.stackoverflow.com/questions/277272/…
ArtOfWarfare
Namun, sudah ada jawaban sederhana di halaman ini. Solusi saya berfungsi dengan baik: stackoverflow.com/a/33531619/3787376 .
Edward
5

Lihat jawaban saya untuk pertanyaan Mengimpor modul dari folder induk untuk informasi terkait, termasuk mengapa jawaban saya tidak menggunakan __file__variabel tidak dapat diandalkan . Solusi sederhana ini harus kompatibel dengan berbagai sistem operasi seperti modul osdaninspect datang sebagai bagian dari Python.

Pertama, Anda perlu untuk bagian impor memeriksa dan os modul.

from inspect import getsourcefile
from os.path import abspath

Selanjutnya, gunakan baris berikut di tempat lain yang diperlukan dalam kode Python Anda:

abspath(getsourcefile(lambda:0))

Bagaimana itu bekerja:

Dari modul bawaan os(uraian di bawah), abspathalat diimpor.

Rutinitas OS untuk Mac, NT, atau Posix tergantung pada sistem apa yang kita gunakan.

Kemudian getsourcefile(uraian di bawah) diimpor dari modul bawaan inspect.

Dapatkan informasi berguna dari objek Python hidup.

  • abspath(path) mengembalikan versi absolut / lengkap dari path file
  • getsourcefile(lambda:0)entah bagaimana mendapatkan file sumber internal objek fungsi lambda, jadi kembali '<pyshell#nn>'ke shell Python atau mengembalikan path file dari kode Python yang saat ini sedang dieksekusi.

Menggunakan abspathpada hasil getsourcefile(lambda:0)harus memastikan bahwa path file yang dihasilkan adalah path file lengkap dari file Python.
Solusi yang dijelaskan ini pada awalnya didasarkan pada kode dari jawaban di Bagaimana saya mendapatkan jalur file yang sedang dieksekusi di Python? .

Edward
sumber
ya saya baru saja datang dengan solusi yang sama ... jauh lebih baik daripada jawaban yang hanya mengatakan itu tidak dapat dilakukan dengan andal ... kecuali pertanyaannya tidak menanyakan apa yang saya pikir itu ...
Pemain Grady
5

Anda hanya menelepon:

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

dari pada:

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

abspath()memberi Anda path absolut dari sys.argv[0](nama file kode Anda) dan dirname()mengembalikan path direktori tanpa nama file.

dgb
sumber
3

Ini harus melakukan trik dengan cara lintas platform (selama Anda tidak menggunakan penerjemah atau semacamnya):

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]adalah direktori tempat skrip panggilan Anda berada (tempat pertama ia mencari modul untuk digunakan oleh skrip itu). Kita dapat mengambil nama file itu sendiri dari ujung sys.argv[0](yang saya lakukan dengan os.path.basename). os.path.joinhanya menyatukan mereka dengan cara lintas platform.os.path.realpathpastikan saja jika kita mendapatkan tautan simbolis dengan nama yang berbeda dari skrip itu sendiri kita masih mendapatkan nama skrip yang sebenarnya.

Saya tidak punya Mac; jadi, saya belum menguji ini pada satu. Tolong beri tahu saya jika itu berfungsi, seperti yang seharusnya. Saya menguji ini di Linux (Xubuntu) dengan Python 3.4. Perhatikan bahwa banyak solusi untuk masalah ini tidak berfungsi pada Mac (karena saya pernah mendengarnya__file__ tidak ada pada Mac).

Perhatikan bahwa jika skrip Anda adalah tautan simbolis, skrip tersebut akan memberi Anda jalur file yang ditautkannya (dan bukan jalur tautan simbolis).

Brōtsyorfuzthrāx
sumber
2

Anda dapat menggunakan Pathdari pathlibmodul:

from pathlib import Path

# ...

Path(__file__)

Anda dapat menggunakan panggilan parentuntuk melangkah lebih jauh di jalur:

Path(__file__).parent
Gavriel Cohen
sumber
Jawaban ini menggunakan __file__variabel yang bisa tidak dapat diandalkan (tidak selalu jalur file lengkap, tidak bekerja pada setiap sistem operasi, dll.) Seperti yang sering disebutkan oleh pengguna StackOverflow. Mengubah jawaban untuk tidak memasukkannya akan menyebabkan lebih sedikit masalah dan lebih kompatibel lintas. Untuk informasi lebih lanjut, lihat stackoverflow.com/a/33532002/3787376 .
Edward
@ mrroot5 Oke, jadi hapus komentar Anda.
Gavriel Cohen
1

Jika kode berasal dari file, Anda bisa mendapatkan nama lengkapnya

sys._getframe().f_code.co_filename

Anda juga dapat mengambil nama fungsi sebagai f_code.co_name

Alex Cohn
sumber
0

Cukup tambahkan berikut ini:

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

Atau:

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

Solusi saya adalah:

import os
print(os.path.dirname(os.path.abspath(__file__)))
ThienSuBS
sumber
-1
import os
current_file_path=os.path.dirname(os.path.realpath('__file__'))
Mohamed.Abdo
sumber
Ini tidak masuk akal. Pertama, '__file__'seharusnya bukan string, kedua, jika Anda melakukannya __file__, ini hanya akan berfungsi untuk file tempat baris kode ini berada, dan bukan file yang dieksekusi?
Andreas Storvik Strauman