Có cách tiêu chuẩn nào để liệt kê tên của các mô-đun Python trong một gói không?


100

Có cách nào đơn giản để liệt kê tên của tất cả các mô-đun trong một gói mà không cần sử dụng __all__không?

Ví dụ, với gói này:

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

Tôi đang tự hỏi liệu có cách chuẩn hoặc cách tích hợp nào để làm điều gì đó như thế này không:

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

Cách tiếp cận thủ công sẽ là lặp qua các đường dẫn tìm kiếm mô-đun để tìm thư mục của gói. Sau đó, người ta có thể liệt kê tất cả các tệp trong thư mục đó, lọc ra các tệp py / pyc / pyo có tên duy nhất, loại bỏ các phần mở rộng và trả lại danh sách đó. Nhưng điều này có vẻ như là một khối lượng công việc hợp lý cho một cái gì đó mà cơ chế nhập mô-đun đã thực hiện nội bộ. Chức năng đó có hiển thị ở bất cứ đâu không?

Câu trả lời:


23

Có lẽ điều này sẽ làm những gì bạn đang tìm kiếm?

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
Tôi sẽ thêm 'và module! = " Init .py"' ​​vào cuối cùng 'if', vì init .py không thực sự là một phần của gói. Và .pyo là một phần mở rộng hợp lệ khác. Ngoài ra, sử dụng imp.find_module là một ý tưởng thực sự tốt; Tôi nghĩ đây là câu trả lời đúng.
DNS

3
Tôi không đồng ý - bạn có thể nhập init trực tiếp, vậy tại sao lại là trường hợp đặc biệt? Nó chắc chắn không đủ đặc biệt để phá vỡ các quy tắc. ;-)
cdleary

6
Bạn có thể nên sử dụng imp.get_suffixes()thay vì danh sách viết tay của mình.
itadok

3
Ngoài ra, lưu ý rằng điều này không hoạt động trên các gói con nhưxml.sax
itsadok

1
Đây là một cách thực sự tồi tệ. Bạn không thể biết chắc chắn đâu là mô-đun từ phần mở rộng tên tệp.
wim

188

Sử dụng python2.3 trở lên , bạn cũng có thể sử dụng pkgutilmô-đun:

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

CHỈNH SỬA: Lưu ý rằng tham số không phải là danh sách các mô-đun, mà là danh sách các đường dẫn, vì vậy bạn có thể muốn thực hiện điều gì đó như sau:

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

15
Điều này đáng lo ngại là không có giấy tờ, nhưng có vẻ như là cách chính xác nhất để làm điều này. Hy vọng bạn không phiền tôi đã thêm ghi chú.
itadok

13
pkgutilcó trong python2.3 trở lên thực sự không . Ngoài ra, trong khi pkgutil.iter_modules()sẽ không hoạt động đệ quy, cũng có một pkgutil.walk_packages(), sẽ đệ quy . Cảm ơn vì con trỏ đến gói này.
Sandip Bhattacharya

Tại sao iter_moduleskhông hoạt động cho nhập khẩu tuyệt đối như thế a.b.testpkgnào? Nó đang cho tôi[]
Hussain

Tôi đã bỏ qua CHỈNH SỬA của bạn :(. Xin lỗi. Nó hoạt động sau khi tôi làm theo đoạn mã thứ hai.
Hussain

1
Tôi không thể xác nhận rằng pkgutil.walk_packages()đệ quy, nó cung cấp cho tôi cùng một đầu ra pkgutil.iter_modules(), vì vậy tôi nghĩ rằng câu trả lời là không đầy đủ.
thứ hai

29
import module
help(module)

2
Mặc dù trợ giúp liệt kê nội dung gói ở cuối văn bản trợ giúp, câu hỏi nằm ở phần cuối của cách thực hiện điều này: f (package_name) => ["module1_name", "module2_name"]. Tôi cho rằng tôi có thể phân tích cú pháp chuỗi được trả về bởi trợ giúp, nhưng điều đó có vẻ vòng vo hơn là liệt kê thư mục.
DNS

1
@DNS: help()in nội dung, nó không trả về một chuỗi.
Junuxx

Tôi đồng ý rằng đây là một cách đường vòng nhưng nó đã đưa tôi xuống một cái hố thỏ để xem cách help()hoạt động. Dù sao, được xây dựng trong pydocmô-đun có thể giúp nhổ ra chuỗi help()Đánh số trang: import pydoc; pydoc.render_doc('mypackage').
sraboy

8

Không biết liệu tôi có đang bỏ qua điều gì không, hay câu trả lời chỉ là lạc hậu nhưng;

Như đã nêu bởi user815423426, điều này chỉ hoạt động với các đối tượng trực tiếp và các mô-đun được liệt kê chỉ là các mô-đun đã được nhập trước đó.

Liệt kê các mô-đun trong một gói có vẻ thực sự dễ dàng bằng cách sử dụng kiểm tra :

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

Tôi đã đặt import = import __ ('myproj.mymod.mysubmod') m = Check.getmembers (i, checks.ismodule) nhưng đường dẫn importd là ~ / myproj / __ init .py và m là danh sách với (mymod, '~ /myproj/mymod/__init__.py ')
hithwen

1
@hithwen Đừng đặt câu hỏi trong phần bình luận, đặc biệt nếu chúng không liên quan trực tiếp. Là một người Samaritanô tốt: Sử dụng imported = import importlib; importlib.import_module('myproj.mymod.mysubmod'). __import__nhập mô-đun cấp cao nhất, xem tài liệu .
siebz0r

Hmm, điều này đầy hứa hẹn nhưng nó không hiệu quả với tôi. Khi tôi làm import inspect, mypackagevà sau đó inspect.getmembers(my_package, inspect.ismodule)tôi nhận được một danh sách trống, mặc dù tôi chắc chắn có nhiều mô-đun khác nhau trong đó.
Amelio Vazquez-Reina

1
Trên thực tế, điều này dường như chỉ hoạt động nếu tôi import my_package.foovà không chỉ import mypackage, trong trường hợp đó, nó sẽ quay trở lại foo. Nhưng điều này đánh bại mục đích
Amelio Vazquez-Reina

3
@ user815423426 Bạn hoàn toàn đúng ;-) Có vẻ như tôi đã bỏ qua điều gì đó.
siebz0r

3

Đây là phiên bản đệ quy hoạt động với python 3.6 trở lên:

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

Lợi ích của việc sử dụng os.scandirlàm trình quản lý ngữ cảnh thay vì lặp lại trực tiếp các mục kết quả là gì?
monkut

1
@monkut Xem docs.python.org/3/library/os.html#os.scandir đó đề nghị sử dụng nó như là một người quản lý bối cảnh để đảm bảo rằng closeđược gọi khi bạn đang thực hiện với nó để đảm bảo rằng bất kỳ tài nguyên được tổ chức được giải phóng.
tacaswell

doesnt làm việc này cho rethay vào đó nó sẽ liệt kê tất cả các gói nhưng thêm re.cho tất cả trong số họ
Tushortz

1

Dựa trên ví dụ của cdleary, đây là đường dẫn liệt kê phiên bản đệ quy cho tất cả các mô-đun con:

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

Điều này sẽ liệt kê các mô-đun:

help("modules")

0

Nếu bạn muốn xem thông tin về gói của mình bên ngoài mã python (từ dấu nhắc lệnh), bạn có thể sử dụng pydoc cho nó.

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

Bạn sẽ có kết quả tương tự như pydoc nhưng bên trong trình thông dịch sử dụng trợ giúp

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

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

Điều đó chỉ hoạt động cho các mô-đun, không phải gói. Hãy thử nó trên logginggói của Python để xem ý tôi là gì. Ghi nhật ký chứa hai mô-đun: trình xử lý và cấu hình. Mã của bạn sẽ trả về một danh sách 66 mục, không bao gồm hai tên đó.
DNS

-3

in dir (mô-đun)


1
Điều đó liệt kê nội dung của một mô-đun đã được nhập. Tôi đang tìm cách liệt kê nội dung của một gói chưa được nhập, giống như 'from x import *' thực hiện khi tất cả không được chỉ định.
DNS

from x import * đầu tiên nhập mô-đun và sau đó sao chép mọi thứ vào mô-đun hiện tại.
Seb

Tôi nhận ra rằng 'from x import *' trên thực tế không nhập các mô-đun con của một gói, vì các vấn đề phân biệt chữ hoa chữ thường trên Windows. Tôi chỉ lấy đó làm ví dụ về những gì tôi muốn làm; Tôi đã chỉnh sửa nó khỏi câu hỏi để tránh nhầm lẫn.
DNS

Điều đó liệt kê tất cả các thuộc tính của một đối tượng đã được nhập, không phải chỉ danh sách các mô-đun con. Vì vậy, nó không trả lời câu hỏi.
bignose
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.