天天看點

Python程式設計:pkgutil擷取包裡面的所有子產品清單

準備工作

環境:

python

3.6

檔案結構

├── clazz
│   ├── __init__.py
│   ├── a.py
│   └── b.py
└── main.py      

a.py的代碼

def show():
    print("show A")      

b.py的代碼

def show():
    print("show B")      

測試開始

1、擷取子產品中的屬性

想要擷取clazz包中a子產品的所包含的方法,可以直接使用dir這個函數,可以看到show這個方法已經包含在其中

from clazz import a

print(dir(a))

"""
['__builtins__', '__cached__', '__doc__', '__file__', 
'__loader__', '__name__', '__package__', '__spec__', 'show']
"""
"""      

2、擷取包中的屬性

如果我要擷取clazz包中所有子產品,直接使用dir并沒有擷取

import clazz

print(dir(clazz))

"""
['__builtins__', '__cached__', '__doc__', '__file__', 
'__loader__', '__name__', '__package__', '__path__', '__spec__']
"""      

既然,子產品可以檢視其中包含的屬性,而包其實就是一個檔案夾

那麼,先擷取封包件夾下的所有子產品檔案,再逐個導入子產品,最後也可以擷取子產品中的屬性。

import os
import importlib


def get_modules(package="."):
    """
    擷取包名下所有非__init__的子產品名
    """
    modules = []
    files = os.listdir(package)

    for file in files:
        if not file.startswith("__"):
            name, ext = os.path.splitext(file)
            modules.append("." + name)

    return modules


if __name__ == '__main__':
    package = "clazz"
    modules = get_modules(package)

    # 将包下的所有子產品,逐個導入,并調用其中的函數
    for module in modules:
        module = importlib.import_module(module, package)
        
        for attr in dir(module):
            if not attr.startswith("__"):
                func = getattr(module, attr)
                func()

    """
    show A
    show B
    """
      

可以看到,我在隻知道包名的情況下,成功擷取了包下所有子產品,和子產品中所有的方法,并成功調用

注意,相對導入的時候需要在子產品名前面加.

but!!!,

Python推薦使用pkgutil.iter_modules(path=None, prefix='')

import pkgutil, clazz

for filefiner, name, ispkg in pkgutil.iter_modules(clazz.__path__, clazz.__name__ + "."):
    print("{0} name: {1:12}, is_sub_package: {2}".format(filefiner, name, ispkg))

"""
FileFinder('/Users/qmp/myproject/mydemo/demo/allclass/clazz') name: clazz.a     , is_sub_package: False
FileFinder('/Users/qmp/myproject/mydemo/demo/allclass/clazz') name: clazz.b     , is_sub_package: False
"""      

函數 iter_modules() 和 walk_packages() 的差別在于:後者會疊代所有深度的子包

import pkgutil, test

for _, name, ispkg in pkgutil.iter_modules(test.__path__, test.__name__ + "."):
    print "name: {0:12}, is_sub_package: {1}".format(name, ispkg)

"""
name: test.a , is_sub_package: True
name: test.add , is_sub_package: False
name: test.b , is_sub_package: True
name: test.user , is_sub_package: False
"""


# 需要加第二個參數 prefix
for _, name, ispkg in pkgutil.walk_packages(test.__path__, test.__name__ + "."):
    print "name: {0:12}, is_sub_package: {1}".format(name, ispkg)
"""
name: test.a , is_sub_package: True
name: test.a.sub , is_sub_package: False
name: test.add , is_sub_package: False
name: test.b , is_sub_package: True
name: test.b.sub , is_sub_package: False
name: test.user , is_sub_package: False
"""      

pkgutil.get_data() 可讀取包内任何檔案内容

pkgutil.get_data("test", "add.py")      

參考

  1. Python 學習筆記 - 子產品