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")
配合动态导包模块一起使用,实现模块的热插拔技术:
当使用import导入Python模块的时候,默认调用的是__import__()
函数。直接使用该函数的情况很少见,一般用于动态加载模块。
__import__(name, globals, locals, fromlist, level)
只有name
是必选参数,其他都是可选参数,一般情况下直接使用name
参数即可。
fromlist
指明需要导入的子模块名,level指定导入方式(相对导入或者绝对导入, 默认两者都支持)。
当模块名为package.module
时,而且fromlist
参数为空时,函数返回顶层的模块,也就是package模块。如果fromlist=[module]
,则返回module
。
所以,下面语句返回的结果是一样的:
-
spam = __import__('spam', globals(), locals(), [], -1)
-
spam = __import__('spam.ham', globals(), locals(), [], -1)
而下面指定了fromlist
,就可以获取到子模块。
-
_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], -1)
-
eggs = _temp.eggs
-
saus = _temp.sausage
如果像直接通过名字导入模块,也可以使用importlib
模块的import_module
函数。
官方推荐使用import_module模块实现导包
可以使用__import__
函数实现模块的延迟导入:
-
class LazyImport:
-
def __init__(self, module_name):
-
self.module_name = module_name
-
self.module = None
-
def __getattr__(self, name):
-
if self.module is None:
-
self.module = __import__(self.module_name)
-
return getattr(self.module, name)
-
string = LazyImport("string")
-
print string.lowercase