Cantumkan semua modul yang merupakan bagian dari paket python?

107

Apakah ada cara mudah untuk menemukan semua modul yang merupakan bagian dari paket python? Saya telah menemukan diskusi lama ini , yang tidak benar-benar konklusif, tetapi saya ingin memiliki jawaban yang pasti sebelum saya meluncurkan solusi saya sendiri berdasarkan os.listdir ().

static_rtti
sumber
6
@ S.Lott: Ada solusi yang lebih umum tersedia, paket python tidak selalu ada di direktori di sistem berkas, tetapi bisa juga di dalam zip.
u0b34a0f6ae
4
mengapa menemukan kembali roda? Jika python memperoleh hypermodules dengan Python 4, pkgutil dan diperbarui dengan itu, kode saya akan tetap berfungsi. Saya suka menggunakan abstraksi yang tersedia. Gunakan metode yang jelas disediakan, itu diuji dan diketahui berhasil. Menerapkan kembali itu .. sekarang Anda harus menemukan dan menangani sendiri setiap kasus sudut.
u0b34a0f6ae
1
@ S.Lott: Jadi setiap kali aplikasi dimulai, itu akan membuka ritsleting telurnya sendiri jika dipasang di dalam satu hanya untuk memeriksa ini? Harap kirimkan tambalan pada proyek saya untuk menemukan kembali roda dalam fungsi ini: git.gnome.org/cgit/kupfer/tree/kupfer/plugins.py#n17 . Harap pertimbangkan kedua telur dan direktori normal, jangan melebihi 20 baris.
u0b34a0f6ae
1
@ S. Lott: Mengapa Anda tidak memahami bahwa ini relevan adalah sesuatu yang tidak dapat Anda pahami. Menemukan ini secara terprogram adalah tentang bahwa aplikasi memperhatikan konten sebuah paket, bukan pengguna.
u0b34a0f6ae
3
Tentu saja yang saya maksud secara terprogram! Jika tidak, saya tidak akan menyebutkan "meluncurkan solusi saya sendiri dengan os.listdir ()"
static_rtti

Jawaban:

145

Ya, Anda menginginkan sesuatu yang berdasarkan pkgutilatau serupa - dengan cara ini Anda dapat memperlakukan semua paket dengan sama terlepas dari apakah mereka berada dalam telur atau ritsleting atau lebih (di mana os.listdir tidak akan membantu).

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)

Bagaimana cara mengimpornya juga? Anda bisa menggunakan __import__seperti biasa:

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
prefix = package.__name__ + "."
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, prefix):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)
    module = __import__(modname, fromlist="dummy")
    print "Imported", module
u0b34a0f6ae
sumber
9
ini importerdikembalikan oleh apa pkgutil.iter_modules? Dapatkah saya menggunakannya untuk mengimpor modul daripada menggunakan ini yang tampaknya "hackish" __import__(modname, fromlist="dummy")?
MestreLion
29
Saya dapat menggunakan importir seperti ini: m = importer.find_module(modname).load_module(modname)dan kemudian madalah modulnya, jadi misalnya:m.myfunc()
chrisleague
@chrisleague Saya menggunakan metode ur dengan python 2.7, tetapi sekarang saya harus melanjutkan dengan python 3.4, jadi Anda tahu bahwa di python 3 pkutil.iter_modules menghasilkan (module_finder, name, ispkg) daripada (module_loader, name, ispkg). Apa yang dapat saya lakukan untuk membuatnya berfungsi seperti yang sebelumnya?
crax
Contoh pertama Anda menghasilkan kesalahan berikut: "AttributeError: 'module' object tidak memiliki atribut ' _path_ '" Apakah ini ada hubungannya dengan versi Python? (Saya menggunakan Python 2.7)
Apostolos
@Apostolos, Anda hanya menggunakan satu garis bawah di kedua sisi jalur (yaitu _path_). Harus ada dua di kedua sisi, dengan total empat (yaitu __path__).
therealmitchconnors
46

Alat yang tepat untuk pekerjaan ini adalah pkgutil.walk_packages.

Untuk mencantumkan semua modul di sistem Anda:

import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=None, onerror=lambda x: None):
    print(modname)

Ketahuilah bahwa walk_packages mengimpor semua subpaket, tetapi bukan submodul.

Jika Anda ingin membuat daftar semua submodul dari paket tertentu maka Anda dapat menggunakan sesuatu seperti ini:

import pkgutil
import scipy
package=scipy
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__,
                                                      prefix=package.__name__+'.',
                                                      onerror=lambda x: None):
    print(modname)

iter_modules hanya mencantumkan modul dengan kedalaman satu tingkat. walk_packages mendapatkan semua submodul. Dalam kasus scipy, misalnya, walk_packages akan dikembalikan

scipy.stats.stats

sementara iter_modules hanya kembali

scipy.stats

Dokumentasi di pkgutil ( http://docs.python.org/library/pkgutil.html ) tidak mencantumkan semua fungsi menarik yang didefinisikan di /usr/lib/python2.6/pkgutil.py.

Mungkin ini berarti fungsi tersebut bukan bagian dari antarmuka "publik" dan dapat berubah.

Namun, setidaknya pada Python 2.6 (dan mungkin versi sebelumnya?) Pkgutil hadir dengan metode walk_packages yang secara rekursif berjalan melalui semua modul yang tersedia.

unutbu
sumber
5
walk_packagessekarang ada di dokumentasi: docs.python.org/library/pkgutil.html#pkgutil.walk_packages
Siput mekanik
1
Contoh kedua Anda menghasilkan kesalahan berikut: "AttributeError: 'module' object tidak memiliki atribut ' _path_ '" - Saya tidak mengujinya dengan 'scipy' tetapi dengan beberapa paket lainnya. Apakah ini ada hubungannya dengan versi Python? (Saya menggunakan Python 2.7)
Apostolos
1
@Apostolos: Harus ada dua garis bawah ( _) sebelum dan sesudah path- yaitu, gunakanpackage.__path__ daripada package._path_. Mungkin lebih mudah untuk mencoba memotong & menempel kode daripada mengetik ulang.
unutbu
Ada dua di antaranya, ketika saya menulis komentar! :) Tapi mereka telah dilucuti oleh sistem. Salahku; Saya harus menempatkan tiga undercores. Tapi kemudian, ini tidak masalah jika saya ingin menggunakan huruf miring, padahal tidak! ... Ini adalah situasi rugi-rugi. :) Pokoknya, ketika saya menjalankan kode saya menggunakan dua di antaranya, tentu saja. (Saya menyalin-tempel kode.)
Apostolos
@Apostolos: Pastikan variabel packagemengarah ke paket, bukan modul. Modul adalah file sedangkan paket adalah direktori. Semua paket memiliki __path__atribut (... kecuali seseorang menghapus atribut karena suatu alasan.)
unutbu
2

Ini bekerja untuk saya:

import types

for key, obj in nltk.__dict__.iteritems():
    if type(obj) is types.ModuleType: 
        print key
DarinP
sumber
1
Ini gagal dalam dua cara 1. paket tidak selalu secara eksplisit mengimpor submodul mereka ke namespace tingkat atas 2. paket dapat mengimpor modul pihak ketiga lainnya ke namespace tingkat atas mereka
wim
0

Saya sedang mencari cara untuk memuat ulang semua submodul yang saya edit langsung di paket saya. Ini adalah kombinasi dari jawaban / komentar di atas, jadi saya memutuskan untuk mempostingnya di sini sebagai jawaban daripada komentar.

package=yourPackageName
import importlib
import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__+'.', onerror=lambda x: None):
    try:
        modulesource = importlib.import_module(modname)
        reload(modulesource)
        print("reloaded: {}".format(modname))
    except Exception as e:
        print('Could not load {} {}'.format(modname, e))
pengguna1767754
sumber
-4

Inilah salah satu cara, di luar kepala saya:

>>> import os
>>> filter(lambda i: type(i) == type(os), [getattr(os, j) for j in dir(os)])
[<module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'errno' (built-in)>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'sys' (built-in)>]

Itu pasti bisa dibersihkan dan diperbaiki.

EDIT: Ini versi yang sedikit lebih bagus:

>>> [m[1] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
[<module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'errno' (built-in)>, <module 'sys' (built-in)>]
>>> [m[0] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
['_copy_reg', 'UserDict', 'path', 'errno', 'sys']

CATATAN: Ini juga akan menemukan modul yang mungkin tidak selalu terletak di subdirektori paket, jika mereka ditarik dalam __init__.pyfilenya, jadi itu tergantung pada apa yang Anda maksud dengan "bagian dari" sebuah paket.

Steve Losh
sumber
maaf, itu tidak ada gunanya. Selain positif palsu, itu hanya akan menemukan submodul paket yang sudah diimpor juga.
u0b34a0f6ae