Bagaimana cara mengambil jalur modul?

757

Saya ingin mendeteksi apakah modul telah berubah. Sekarang, menggunakan inotify itu sederhana, Anda hanya perlu tahu direktori tempat Anda ingin mendapatkan notifikasi.

Bagaimana cara mengambil jalur modul dengan python?

Riang
sumber
9
Jika Anda masih mencari di situs ini, perbarui jawaban yang benar untuk ini . Ini jauh lebih bersih daripada solusi yang diusulkan dan bekerja juga dalam kasus di mana __file__tidak ditetapkan.
erikbwork
3
@ erikb85: tidak hanya bersih; inspectsolusi berbasis juga berfungsi untuk execfile()kasus ketika __file__menghasilkan nama yang salah diam-diam.
jfs
import pathlib, module; pathlib.Path(module.__file__).resolve().parentIni adalah platformindependen
MortenB

Jawaban:

907
import a_module
print(a_module.__file__)

Akan benar-benar memberi Anda jalan ke file .pyc yang dimuat, setidaknya di Mac OS X. Jadi saya kira Anda bisa melakukannya:

import os
path = os.path.abspath(a_module.__file__)

Anda juga dapat mencoba:

path = os.path.dirname(a_module.__file__)

Untuk mendapatkan direktori modul.

orestis
sumber
54
ini menjawab cara mendapatkan lintasan modul yang Anda impor, tetapi bukan dari modul / skrip tempat Anda berada (untuk skrip yang Anda jalankan, __file__bukan lintasan lengkap, ini relatif). Untuk file yang saya masuk saya harus mengimpor modul lain dari direktori yang sama dan lakukan seperti yang ditunjukkan di sini. Adakah yang tahu cara yang lebih nyaman?
Ben Bryant
5
@hbdgaf cukup yakin tidak ada yang namanya built-inself.__file__
Dan Passaro
26
@BenBryant @hbdgaf os.path.dirname(__file__)berfungsi dengan baik untuk saya dan mengembalikan jalur abs dari direktori modul.
Niccolò
9
Saya mencoba melakukan ini dan mendapatkan traceback: AttributeError: 'module' object has no attribute '__file__'
Dorian Dore
5
@DorianDore Saya sedikit mengotak-atik modul dan mendapatkan solusi path = module.__path__.__dict__["_path"][0], tapi saya tidak yakin apakah ini portabel atau jika tidak berbeda antara versi python juga. Ini bekerja untuk saya, tidak seperti jawaban ini yang memberi saya kesalahan dan inspectjawaban yang sama menimbulkan TypeError: <module 'module' (namespace)> is a built-in module...
Jezor
279

Ada inspectmodul dalam python.

Dokumentasi resmi

Modul inspect menyediakan beberapa fungsi yang berguna untuk membantu mendapatkan informasi tentang objek langsung seperti modul, kelas, metode, fungsi, traceback, objek bingkai, dan objek kode. Misalnya, ini dapat membantu Anda memeriksa konten kelas, mengambil kode sumber metode, mengekstrak dan memformat daftar argumen untuk suatu fungsi, atau mendapatkan semua informasi yang Anda butuhkan untuk menampilkan traceback terperinci.

Contoh:

>>> import os
>>> import inspect
>>> inspect.getfile(os)
'/usr/lib64/python2.7/os.pyc'
>>> inspect.getfile(inspect)
'/usr/lib64/python2.7/inspect.pyc'
>>> os.path.dirname(inspect.getfile(inspect))
'/usr/lib64/python2.7'
Tomas Tomecek
sumber
7
Anda dapat menggunakan inspectuntuk mendapatkan nama file saat ini juga; lihat stackoverflow.com/a/50905/320036
z0r
5
Saya telah menelusuri pertanyaan ini berulang kali dan ini adalah jawaban paling masuk akal yang pernah saya lihat! Harap perbarui informasi tentanginspect.currentframe()
erikbwork
yang inspect.getfile()pendekatan tidak bekerja dengan modul _io, tetapi bekerja dengan modul io.
smwikipedia
70

Seperti jawaban lain katakan, cara terbaik untuk melakukan ini adalah dengan __file__(ditunjukkan lagi di bawah). Namun, ada peringatan penting, yaitu __file__TIDAK ada jika Anda menjalankan modul sendiri (yaitu sebagai __main__).

Misalnya, Anda memiliki dua file (keduanya ada di PYTHONPATH Anda):

#/path1/foo.py
import bar
print(bar.__file__)

dan

#/path2/bar.py
import os
print(os.getcwd())
print(__file__)

Menjalankan foo.py akan memberikan output:

/path1        # "import bar" causes the line "print(os.getcwd())" to run
/path2/bar.py # then "print(__file__)" runs
/path2/bar.py # then the import statement finishes and "print(bar.__file__)" runs

NAMUN jika Anda mencoba menjalankan bar.py sendiri, Anda akan mendapatkan:

/path2                              # "print(os.getcwd())" still works fine
Traceback (most recent call last):  # but __file__ doesn't exist if bar.py is running as main
  File "/path2/bar.py", line 3, in <module>
    print(__file__)
NameError: name '__file__' is not defined 

Semoga ini membantu. Peringatan ini menghabiskan banyak waktu dan kebingungan saat menguji solusi lain yang disajikan.

mcstrother
sumber
5
Dalam hal ini, Anda dapat menggunakan sys.argv [0] sebagai pengganti file .
Jimothy
4
Apakah itu peringatan versi khusus? Di 2.6 dan 2.7, saya berhasil mengandalkan file , yang berfungsi file ketika nama __ == '__ main '. Satu-satunya kasus kegagalan yang pernah saya lihat adalah dengan "python -c 'print file '". Saya akan menambahkan bahwa terkadang file dapat berupa '<stdin>', yang terjadi ketika IDE seperti emacs mengeksekusi buffer saat ini.
Paul Du Bois
1
Perhatikan bahwa karakter "__" memimpin-dan-mengekor menempatkan kata dalam huruf tebal, jadi ingatlah itu ketika membaca komentar sebelumnya :-P
Paul Du Bois
1
@PaulDuBois Anda dapat mengelilinginya dengan back tics: ` __file__` menjadi__file__
fncomp
1
Bagaimana Anda mendapatkannya NameError? @ Paulus Du Bois: Saya sudah mencoba Python 2,3-3,4 dan __file__didefinisikan namun saya menjalankan file Python: python a.py, python -ma, ./a.py.
jfs
43

Saya akan mencoba menangani beberapa variasi pada pertanyaan ini juga:

  1. menemukan jalur skrip yang disebut
  2. menemukan jalur skrip yang sedang dijalankan
  3. menemukan direktori skrip yang dipanggil

(Beberapa pertanyaan ini telah diajukan pada SO, tetapi telah ditutup sebagai duplikat dan dialihkan ke sini.)

Peringatan Penggunaan __file__

Untuk modul yang telah Anda impor:

import something
something.__file__ 

akan mengembalikan jalur absolut modul. Namun, mengingat skrip berikut foo.py:

#foo.py
print '__file__', __file__

Menyebutnya dengan 'python foo.py' Akan kembali hanya 'foo.py'. Jika Anda menambahkan shebang:

#!/usr/bin/python 
#foo.py
print '__file__', __file__

dan menyebutnya dengan ./foo.py, ia akan mengembalikan './foo.py'. Memanggilnya dari direktori yang berbeda, (misalnya memasukkan foo.py di bilah direktori), lalu memanggil keduanya

python bar/foo.py

atau menambahkan shebang dan mengeksekusi file secara langsung:

bar/foo.py

akan mengembalikan 'bar / foo.py' ( jalur relatif ).

Menemukan direktori

Sekarang pergi dari sana untuk mendapatkan direktori, os.path.dirname(__file__)juga bisa rumit. Paling tidak di sistem saya, itu mengembalikan string kosong jika Anda memanggilnya dari direktori yang sama dengan file. ex.

# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)

akan menampilkan:

__file__ is: foo.py
os.path.dirname(__file__) is: 

Dengan kata lain, ini mengembalikan string kosong, jadi ini sepertinya tidak dapat diandalkan jika Anda ingin menggunakannya untuk file saat ini (sebagai lawan dari file modul yang diimpor). Untuk menyiasatinya, Anda dapat membungkusnya dalam panggilan ke abspath:

# foo.py
import os
print 'os.path.abspath(__file__) is:', os.path.abspath(__file__)
print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__))

yang menghasilkan sesuatu seperti:

os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar

Perhatikan bahwa abspath () TIDAK menyelesaikan symlink. Jika Anda ingin melakukan ini, gunakan realpath () sebagai gantinya. Misalnya, membuat symlink file_import_testing_link mengarah ke file_import_testing.py, dengan konten berikut:

import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)

mengeksekusi akan mencetak path absolut seperti:

abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py

file_import_testing_link -> file_import_testing.py

Menggunakan memeriksa

@SummerBreeze menyebutkan menggunakan memeriksa modul.

Ini tampaknya bekerja dengan baik, dan cukup ringkas, untuk modul yang diimpor:

import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)

dengan patuh mengembalikan jalur absolut. Namun untuk menemukan jalur skrip yang sedang dijalankan, saya tidak melihat cara untuk menggunakannya.

jpgeek
sumber
1
inspect.getfile (os) sama dengan os .__ file__ dari kode: def getfile (objek): "" "Cari sumber atau file terkompilasi mana objek didefinisikan." "" jika ismodule (objek): if hasattr (objek, ' file '): return object .__ file__
idanzalz
4
Anda dapat menggunakan inspect.getfile(inspect.currentframe())untuk mendapatkan jalur skrip yang sedang berjalan.
jbochi
33

Saya tidak mengerti mengapa tidak ada yang membicarakan hal ini, tetapi bagi saya solusi paling sederhana adalah menggunakan imp.find_module ("modulename") (dokumentasi di sini ):

import imp
imp.find_module("os")

Ini memberi tuple dengan jalan di posisi kedua:

(<open file '/usr/lib/python2.7/os.py', mode 'U' at 0x7f44528d7540>,
'/usr/lib/python2.7/os.py',
('.py', 'U', 1))

Keuntungan dari metode ini daripada metode "inspeksi" adalah Anda tidak perlu mengimpor modul untuk membuatnya berfungsi, dan Anda dapat menggunakan string dalam input. Berguna saat memeriksa modul yang dipanggil dalam skrip lain misalnya.

EDIT :

Dalam python3, importlibmodul harus melakukan:

Dokumen dari importlib.util.find_spec:

Kembalikan spesifikasi untuk modul yang ditentukan.

Pertama, sys.modules diperiksa untuk melihat apakah modul sudah diimpor. Jika demikian, maka sys.modules [nama]. spec dikembalikan. Jika itu diatur ke Tidak Ada, maka ValueError dinaikkan. Jika modul tidak ada di sys.modules, maka sys.meta_path dicari untuk spesifikasi yang sesuai dengan nilai 'path' yang diberikan kepada para pencari. Tidak ada yang dikembalikan jika tidak ada spek yang dapat ditemukan.

Jika nama untuk submodule (berisi titik), modul induk diimpor secara otomatis.

Argumen nama dan paket bekerja sama dengan importlib.import_module (). Dengan kata lain, nama modul relatif (dengan titik utama) berfungsi.

PlasmaBinturong
sumber
2
imp TIDAK disusutkan dalam python 2 (versi saat ini 2.7.13). imp disusutkan dalam python 3 sejak 3.4. importlib akan digunakan dalam python 3 sebagai gantinya. Saya suka solusi ini, karena bahkan berfungsi ketika impor aktual akan gagal (mis. Karena modul 64bit untuk mesin 32bit)
mdew
Dan BENAR-BENAR bagus ketika mencoba menemukan jalan 64 bit sqlite3 dan impor gagal. Sempurna.
rahvin_t
2
importlib.machinery.PathFinder().find_module("os").get_filename()Alternatif terpendek yang imp.find_modulesaya temukan di Python3 +. Jika ada yang mencari penggunaan importlib.util.find_spec.
Torxed
Metode ini berfungsi tanpa mengimpor modul yang sebenarnya, yang sangat bagus karena saya menggunakan ini untuk mengetahui versi modul mana yang sedang diimpor dari komputer bersama.
Gouda
19

Ini sepele.

Setiap modul memiliki __file__variabel yang menunjukkan jalur relatifnya dari tempat Anda berada sekarang.

Oleh karena itu, mendapatkan direktori untuk modul untuk memberi tahu itu sederhana seperti:

os.path.dirname(__file__)
Riang
sumber
14
Hampir tapi tidak benar - File ini tidak "relatif terhadap mana Anda berada di sekarang"; ketika itu relatif (yang akan hanya ketika ada jalur relatif di sys.path), itu relatif ke tempat Anda berada ketika modul dimuat .
Charles Duffy
17
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)
vinoth
sumber
1
tidak berfungsi pada Linux python 2.6 saya karena __file__hanya dir / test.py, abspath melibatkan cwd untuk melengkapi pathname yang bukan hasil yang diinginkan, tetapi jika Anda mengimpor modul maka m.__file__berikan hasil yang diinginkan.
Ben Bryant
10
import module
print module.__path__

Paket mendukung satu atribut khusus lagi __path__,. Ini diinisialisasi menjadi daftar yang berisi nama direktori yang menyimpan paket __init__.pysebelum kode dalam file tersebut dieksekusi. Variabel ini dapat dimodifikasi; hal itu memengaruhi pencarian modul dan subpackage di masa depan yang terkandung dalam paket.

Meskipun fitur ini tidak sering dibutuhkan, fitur ini dapat digunakan untuk memperluas rangkaian modul yang ditemukan dalam sebuah paket.

Sumber

Lukas Greblikas
sumber
9

Utilitas Baris Perintah

Anda dapat men-tweak ke utilitas baris perintah,

python-which <package name>

masukkan deskripsi gambar di sini


Membuat /usr/local/bin/python-which

#!/usr/bin/env python

import importlib
import os
import sys

args = sys.argv[1:]
if len(args) > 0:
    module = importlib.import_module(args[0])
    print os.path.dirname(module.__file__)

Jadikan itu dapat dieksekusi

sudo chmod +x /usr/local/bin/python-which
Jossef Harush
sumber
7

Jadi saya menghabiskan cukup banyak waktu untuk mencoba melakukan ini dengan py2exe Masalahnya adalah untuk mendapatkan folder dasar dari skrip apakah itu dijalankan sebagai skrip python atau sebagai py2exe dieksekusi. Juga untuk membuatnya berfungsi apakah sedang dijalankan dari folder saat ini, folder lain atau (ini yang paling sulit) dari jalur sistem.

Akhirnya saya menggunakan pendekatan ini, menggunakan sys.frozen sebagai indikator berjalan di py2exe:

import os,sys
if hasattr(sys,'frozen'): # only when running in py2exe this exists
    base = sys.prefix
else: # otherwise this is a regular python script
    base = os.path.dirname(os.path.realpath(__file__))
uri
sumber
7

Anda cukup mengimpor modul Anda lalu tekan namanya dan Anda akan mendapatkan path lengkapnya

>>> import os
>>> os
<module 'os' from 'C:\\Users\\Hassan Ashraf\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\os.py'>
>>>
Hassan Ashraf
sumber
4

Jika Anda ingin mengambil jalur root paket dari salah satu modulnya, berikut ini berfungsi (diuji pada Python 3.6):

from . import __path__ as ROOT_PATH
print(ROOT_PATH)

Jalur utama __init__.pyjuga dapat dirujuk dengan menggunakan __file__sebagai gantinya.

Semoga ini membantu!

fr_andres
sumber
3

Jika satu-satunya peringatan menggunakan __file__adalah ketika saat ini, direktori relatif kosong (yaitu, ketika berjalan sebagai skrip dari direktori yang sama di mana skrip berada), maka solusi sepele adalah:

import os.path
mydir = os.path.dirname(__file__) or '.'
full  = os.path.abspath(mydir)
print __file__, mydir, full

Dan hasilnya:

$ python teste.py 
teste.py . /home/user/work/teste

Caranya adalah or '.'setelah dirname()panggilan. Ini menetapkan dir sebagai ., yang berarti direktori saat ini dan merupakan direktori yang valid untuk setiap fungsi yang berhubungan dengan jalur.

Jadi, menggunakan abspath()itu tidak benar-benar diperlukan. Tetapi jika Anda tetap menggunakannya, triknya tidak diperlukan: abspath()menerima jalur kosong dan menafsirkannya dengan benar sebagai direktori saat ini.

MestreLion
sumber
Lebih baik menukar pesanan dan hanya menggunakan os.path.dirname(os.path.abspath(__file__))karena Anda tidak perlu khawatir tentang jalur relatif sama sekali. Satu peluang lebih sedikit untuk bug.
szali
3

Saya ingin berkontribusi dengan satu skenario umum (dalam Python 3) dan mengeksplorasi beberapa pendekatan untuk itu.

Fungsi built-in open () menerima jalur relatif atau absolut sebagai argumen pertama. Jalur relatif diperlakukan sebagai relatif terhadap direktori kerja saat ini, sehingga disarankan untuk meneruskan jalur absolut ke file.

Sederhananya, jika Anda menjalankan file skrip dengan kode berikut, itu tidak dijamin bahwa example.txtfile tersebut akan dibuat di direktori yang sama di mana file skrip berada:

with open('example.txt', 'w'):
    pass

Untuk memperbaiki kode ini kita perlu mendapatkan path ke skrip dan membuatnya absolut. Untuk memastikan path menjadi absolut, cukup gunakan fungsi os.path.realpath () . Untuk mendapatkan lintasan ke skrip ada beberapa fungsi umum yang mengembalikan berbagai hasil lintasan:

  • os.getcwd()
  • os.path.realpath('example.txt')
  • sys.argv[0]
  • __file__

Kedua fungsi os.getcwd () dan os.path.realpath () mengembalikan hasil jalur berdasarkan direktori kerja saat ini . Umumnya bukan yang kita inginkan. Elemen pertama dari daftar sys.argv adalah jalur skrip root (skrip yang Anda jalankan) terlepas dari apakah Anda memanggil daftar itu di skrip root itu sendiri atau di salah satu modulnya. Mungkin berguna dalam beberapa situasi. The __FILE__ variabel berisi path dari modul yang telah disebut.


Kode berikut dengan benar membuat file example.txtdi direktori yang sama di mana skrip berada:

filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')

with open(filepath, 'w'):
    pass
Jeyekomon
sumber
3

Jika Anda ingin mengetahui path absolut dari skrip Anda, Anda dapat menggunakan objek Path :

from pathlib import Path

print(Path().absolute())
print(Path().resolve('.'))
print(Path().cwd())

metode cwd ()

Kembalikan objek jalur baru yang mewakili direktori saat ini (seperti yang dikembalikan oleh os.getcwd ())

metode mengatasi ()

Jadikan path absolut, selesaikan semua symlink. Objek jalur baru dikembalikan:

Vlad Bezden
sumber
1

Dari dalam modul paket python saya harus merujuk ke file yang berada di direktori yang sama dengan paket. Ex.

some_dir/
  maincli.py
  top_package/
    __init__.py
    level_one_a/
      __init__.py
      my_lib_a.py
      level_two/
        __init__.py
        hello_world.py
    level_one_b/
      __init__.py
      my_lib_b.py

Jadi di atas saya harus memanggil maincli.py dari modul my_lib_a.py mengetahui bahwa top_package dan maincli.py berada di direktori yang sama. Inilah cara saya mendapatkan jalur ke maincli.py:

import sys
import os
import imp


class ConfigurationException(Exception):
    pass


# inside of my_lib_a.py
def get_maincli_path():
    maincli_path = os.path.abspath(imp.find_module('maincli')[1])
    # top_package = __package__.split('.')[0]
    # mod = sys.modules.get(top_package)
    # modfile = mod.__file__
    # pkg_in_dir = os.path.dirname(os.path.dirname(os.path.abspath(modfile)))
    # maincli_path = os.path.join(pkg_in_dir, 'maincli.py')

    if not os.path.exists(maincli_path):
        err_msg = 'This script expects that "maincli.py" be installed to the '\
        'same directory: "{0}"'.format(maincli_path)
        raise ConfigurationException(err_msg)

    return maincli_path

Berdasarkan posting oleh PlasmaBinturong saya mengubah kode.

Al Conrad
sumber
1

Jika Anda ingin melakukan ini secara dinamis dalam "program" coba kode ini:
Maksud saya adalah, Anda mungkin tidak tahu nama pasti modul untuk "hardcode" itu. Itu dapat dipilih dari daftar atau mungkin saat ini sedang berjalan untuk menggunakan __file__.

(Saya tahu, itu tidak akan berfungsi dalam Python 3)

global modpath
modname = 'os' #This can be any module name on the fly
#Create a file called "modname.py"
f=open("modname.py","w")
f.write("import "+modname+"\n")
f.write("modpath = "+modname+"\n")
f.close()
#Call the file with execfile()
execfile('modname.py')
print modpath
<module 'os' from 'C:\Python27\lib\os.pyc'>

Saya mencoba untuk menyingkirkan masalah "global" tetapi menemukan kasus-kasus di mana itu tidak berfungsi saya pikir "execfile ()" dapat ditiru dengan Python 3 Karena ini dalam sebuah program, dapat dengan mudah dimasukkan ke dalam metode atau modul untuk penggunaan kembali.

Robin Randall
sumber
0

Jika Anda menginstalnya menggunakan pip, "pip show" berfungsi dengan baik ('Lokasi')

$ pip tampilkan detectron2

Name: detectron2
Version: 0.1
Summary: Detectron2 is FAIR next-generation research platform for object detection and segmentation.
Home-page: https://github.com/facebookresearch/detectron2
Author: FAIR
Author-email: None
License: UNKNOWN
Location: /home/ubuntu/anaconda3/envs/pytorch_p36/lib/python3.6/site-packages
Requires: yacs, tabulate, tqdm, pydot, tensorboard, Pillow, termcolor, future, cloudpickle, matplotlib, fvcore
Javi
sumber
0

Berikut ini adalah skrip bash cepat jika bermanfaat bagi siapa pun. Saya hanya ingin dapat mengatur variabel lingkungan sehingga saya dapat pushdke kode.

#!/bin/bash
module=${1:?"I need a module name"}

python << EOI
import $module
import os
print os.path.dirname($module.__file__)
EOI

Contoh shell:

[root@sri-4625-0004 ~]# export LXML=$(get_python_path.sh lxml)
[root@sri-4625-0004 ~]# echo $LXML
/usr/lib64/python2.7/site-packages/lxml
[root@sri-4625-0004 ~]#
shrewmouse
sumber