Bir pakette Python modüllerinin adlarını listelemenin standart bir yolu var mı?


102

Bir paketteki tüm modüllerin adlarını kullanmadan listelemenin basit bir yolu var mı __all__?

Örneğin, bu paket verildiğinde:

/testpkg
/testpkg/__init__.py
/testpkg/modulea.py
/testpkg/moduleb.py

Bunun gibi bir şey yapmanın standart veya yerleşik bir yolu olup olmadığını merak ediyorum:

>>> package_contents("testpkg")
['modulea', 'moduleb']

Manuel yaklaşım, paketin dizinini bulmak için modül arama yollarını yinelemek olacaktır. Daha sonra bu dizindeki tüm dosyalar listelenebilir, benzersiz şekilde adlandırılmış py / pyc / pyo dosyalarını filtreleyebilir, uzantıları çıkarabilir ve bu listeyi geri döndürebilir. Ancak bu, modül içe aktarma mekanizmasının zaten dahili olarak yaptığı bir şey için makul miktarda çalışma gibi görünüyor. Bu işlevsellik herhangi bir yerde açığa çıkıyor mu?

Yanıtlar:


24

Belki bu aradığınız şeyi yapar?

import imp
import os
MODULE_EXTENSIONS = ('.py', '.pyc', '.pyo')

def package_contents(package_name):
    file, pathname, description = imp.find_module(package_name)
    if file:
        raise ImportError('Not a package: %r', package_name)
    # Use a set because some may be both source and compiled.
    return set([os.path.splitext(module)[0]
        for module in os.listdir(pathname)
        if module.endswith(MODULE_EXTENSIONS)])

1
Ben eklemek 've modül! = 'Olur init .py'' beri 'eğer' finale, init .py gerçekten paketin bir parçası değildir. Ve .pyo başka bir geçerli uzantıdır. Bunun dışında imp.find_module kullanmak gerçekten iyi bir fikirdir; Bunun doğru cevap olduğunu düşünüyorum.
DNS

3
Katılmıyorum - init'i doğrudan içe aktarabilirsiniz , öyleyse neden özel durumda? Kesinlikle kuralları çiğnemek için yeterince özel değil. ;-)
cdleary

6
imp.get_suffixes()El yazısı listeniz yerine muhtemelen kullanmalısınız .
itsadok

3
Ayrıca, bunun gibi alt paketlerde çalışmadığını unutmayınxml.sax
itsadok

1
Bu gerçekten kötü bir yol. Dosya adı uzantısından bir modülün ne olduğunu güvenilir bir şekilde anlayamazsınız.
wim

193

Python2.3 ve üstünü kullanarak pkgutilmodülü de kullanabilirsiniz :

>>> import pkgutil
>>> [name for _, name, _ in pkgutil.iter_modules(['testpkg'])]
['modulea', 'moduleb']

DÜZENLEME: Parametrenin bir modül listesi değil, bir yol listesi olduğuna dikkat edin, bu nedenle şuna benzer bir şey yapmak isteyebilirsiniz:

>>> import os.path, pkgutil
>>> import testpkg
>>> pkgpath = os.path.dirname(testpkg.__file__)
>>> print [name for _, name, _ in pkgutil.iter_modules([pkgpath])]

16
Bu rahatsız edici bir şekilde belgelenmemiş, ancak bunu yapmanın en doğru yolu gibi görünüyor. Umarım notu eklememin sakıncası yoktur.
itsadok

13
pkgutilpython2.3 ve üstü sürümlerde var . Ayrıca, pkgutil.iter_modules()özyinelemeli çalışmayacak olsa da, bir pkgutil.walk_packages()de var, bu da tekrar edecek . Yine de bu pakete işaretçi için teşekkürler.
Sandip Bhattacharya

Neden iter_modulesmutlak ithalat için çalışmıyor gibi a.b.testpkg? Bana veriyor[]
Hussain

DÜZENLEMENİZİ gözden kaçırdım :(. Üzgünüm. İkinci parçayı takip ettikten sonra çalışıyor.
Hussain

1
pkgutil.walk_packages()Yinelemeleri teyit edemiyorum, bana aynı çıktıyı veriyor pkgutil.iter_modules(), bu yüzden cevabın eksik olduğunu düşünüyorum.
ilk

29
import module
help(module)

2
Yardım, yardım metninin altında paket içeriğini listelese de, soru daha çok bunun nasıl yapılacağına ilişkin satırlardır: f (paket_adı) => ["modül1_adı", "modül2_adı"]. Sanırım yardım tarafından döndürülen dizeyi ayrıştırabilirim, ancak bu dizini listelemekten daha dolambaçlı görünüyor.
DNS

1
@DNS: bir help()şeyler yazdırır, bir dize döndürmez.
Junuxx

Bunun dolambaçlı bir yol olduğuna katılıyorum ama nasıl help()çalıştığını görmek için beni bir tavşan deliğine gönderdi . Neyse, yerleşik pydocmodülü mü dize tükürmek yardımcı olabilir help()Paginates: import pydoc; pydoc.render_doc('mypackage').
sraboy

9

Bir şeyi gözden kaçırıp atmadığımı veya cevapların tarihi geçmiş mi bilmiyorum ama;

User815423426 tarafından belirtildiği gibi, bu yalnızca canlı nesneler için çalışır ve listelenen modüller yalnızca daha önce içe aktarılmış modüllerdir.

Bir paketteki modülleri Listeleme gerçekten kolay kullanarak görünüyor incelemek :

>>> import inspect, testpkg
>>> inspect.getmembers(testpkg, inspect.ismodule)
['modulea', 'moduleb']

Import = import __ ('myproj.mymod.mysubmod') m = inspect.getmembers (i, inspect.ismodule) koydum ama importd yolu ~ / myproj / __ init .py ve m (mymod, '~ ile bir listedir) /myproj/mymod/__init__.py ')
hithwen

1
@hithwen Yorumlarda soru sormayın, özellikle doğrudan ilişkili değilse. İyi bir Samiriyeli olmak: Kullanın imported = import importlib; importlib.import_module('myproj.mymod.mysubmod'). __import__en üst düzey modülü içe aktarır, belgelere bakın .
siebz0r

Hmm, bu umut verici ama benim için çalışmıyor. Bunu yaptığımda import inspect, mypackageve daha sonra inspect.getmembers(my_package, inspect.ismodule)içinde çeşitli modüller olmasına rağmen boş bir liste alıyorum.
Amelio Vazquez-Reina

1
Aslında, bu sadece işe yarıyor gibi görünüyor, sadece ben import my_package.foodeğil import mypackage, bu durumda geri dönüyor foo. Ama bu amacın
dışına çıkıyor

3
@ user815423426 Kesinlikle haklısın ;-) Bir şeyi gözden kaçırmışım gibi görünüyor.
siebz0r

3

Bu, python 3.6 ve üzeri ile çalışan yinelemeli bir sürümdür:

import importlib.util
from pathlib import Path
import os
MODULE_EXTENSIONS = '.py'

def package_contents(package_name):
    spec = importlib.util.find_spec(package_name)
    if spec is None:
        return set()

    pathname = Path(spec.origin).parent
    ret = set()
    with os.scandir(pathname) as entries:
        for entry in entries:
            if entry.name.startswith('__'):
                continue
            current = '.'.join((package_name, entry.name.partition('.')[0]))
            if entry.is_file():
                if entry.name.endswith(MODULE_EXTENSIONS):
                    ret.add(current)
            elif entry.is_dir():
                ret.add(current)
                ret |= package_contents(current)


    return ret

os.scandirDoğrudan sonuç girişlerini yinelemek yerine bir bağlam yöneticisi olarak kullanmanın avantajı nedir ?
monkut

1
Bkz @monkut docs.python.org/3/library/os.html#os.scandir emin olmak için bir bağlam yöneticisi olarak kullanan öneririz closeherhangi bekletilen kaynakları serbest sağlamak için onunla bittiğinde denir.
tacaswell

bu işe yaramıyor rebunun yerine her paketi listeliyor ama re.hepsine ekliyor
Tushortz

1

Cdleary örneğine göre, tüm alt modüller için yinelemeli bir sürüm listeleme yolu:

import imp, os

def iter_submodules(package):
    file, pathname, description = imp.find_module(package)
    for dirpath, _, filenames in os.walk(pathname):
        for  filename in filenames:
            if os.path.splitext(filename)[1] == ".py":
                yield os.path.join(dirpath, filename)

0

Bu, modülleri listelemelidir:

help("modules")

0

Python kodunun dışında (bir komut isteminden) paketinizle ilgili bir bilgi görüntülemek isterseniz, bunun için pydoc'u kullanabilirsiniz.

# get a full list of packages that you have installed on you machine
$ python -m pydoc modules

# get information about a specific package
$ python -m pydoc <your package>

Pydoc ile aynı sonuca sahip olacaksınız ancak yardım kullanarak tercümanın içinde

>>> import <my package>
>>> help(<my package>)

-3

print dir (modül)


1
Bu, zaten içe aktarılmış olan bir modülün içeriğini listeler. Henüz içe aktarılmamış bir paketin içeriğini listelemenin bir yolunu arıyorum, tıpkı 'x içe aktarmadan *' her şey belirtilmediğinde yaptığı gibi.
DNS

x import * 'dan önce modülü içe aktarır ve ardından her şeyi geçerli modüle kopyalar.
Seb

Windows'daki büyük / küçük harfe duyarlılık sorunları nedeniyle "x içe aktarmadan *" öğesinin aslında bir paketin alt modüllerini içe aktarmadığını fark ettim. Bunu sadece yapmak istediğim şeye örnek olarak ekledim; Karışıklığı önlemek için onu söz konusu dışında düzenledim.
DNS

Bu, yalnızca alt modüllerin listesini değil, önceden içe aktarılmış bir nesnenin tüm özelliklerini listeler. Yani soruya cevap vermiyor.
bignose

-3
def package_contents(package_name):
  package = __import__(package_name)
  return [module_name for module_name in dir(package) if not module_name.startswith("__")]

Bu sadece modüller için çalışır, paketler için değil. loggingNe demek istediğimi görmek için Python'un paketinde deneyin . Günlük kaydı iki modül içerir: işleyiciler ve yapılandırma. Kodunuz, bu iki adı içermeyen 66 öğelik bir liste döndürecektir.
DNS
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.