python动态加载所有class或所有module
2020年6月23日 - 由Bo 0 评论 2069 阅读
如下场景:在一个package下有多个文件,每个文件里都有对应的class,我并不想写多行"from myModule import myClass"也不想每次新增了文件或者类还得去新增一行import,希望能dynamic import即是一次import便能动态加载所有的类或者加载所有的模块。
对于ruby有一个require_all模块挺好用的,但Python没有类似的。
比如现在有一些文件,目录结构为:
- test_base
|
-- __init__.py
-- page_objects
|
--- __init__.py
--- login_page.py
|
---- LoginPage Class
|
--- article_page.py
|
---- ArticlePage Class
...
...
对python来说,可以从__init__.py入手。
当一个文件夹下有__init__.py时,这个文件夹便被python当做package看待。
当我们import一个package时,__init__.py会做一些初始化的预操作,导入package时实际是导入的这个__init__.py,在__init__.py文件中的代码会在import时执行。
比如当__int__.py未添加修改前:
>>> from projects.byblog.pages import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> from projects.byblog import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'pages']
>>>
当需要动态加载所有class时,可以在__init__.py中添加代码:
import os, sys
path = os.path.dirname(os.path.abspath(__file__))
for py in [f[:-3] for f in os.listdir(path) if f.endswith('.py') and f != '__init__.py']:
mod = __import__('.'.join([__name__, py]), fromlist=[py])
classes = [getattr(mod, x) for x in dir(mod) if isinstance(getattr(mod, x), type)]
print(classes)
for cls in classes:
setattr(sys.modules[__name__], cls.__name__, cls)
当我们import时,输出就会变成:
>>> from projects.byblog.pages import *
[<class 'projects.byblog.pages.common_component.CommonComponent'>, <class 'utils.mysql_helper.MysqlConnection'>, <class 'common.test_base.page_base.PageBase'>]
[<class 'projects.byblog.pages.common_component.CommonComponent'>, <class 'projects.byblog.pages.post_detail_page.PostDetailPage'>]
[<class 'projects.byblog.pages.common_component.CommonComponent'>, <class 'projects.byblog.pages.login_page.LoginPage'>]
[<class 'projects.byblog.pages.common_component.CommonComponent'>, <class 'projects.byblog.pages.post_list_page.PostListPage'>]
[<class 'projects.byblog.pages.common_component.CommonComponent'>, <class 'projects.byblog.pages.main_page.MainPage'>]
>>> dir()
['CommonComponent', 'LoginPage', 'MainPage', 'MysqlConnection', 'PageBase', 'PostDetailPage', 'PostListPage', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'classes', 'cls', 'common_component', 'login_page', 'main_page', 'mod', 'os', 'path', 'post_detail_page', 'post_list_page', 'py', 'sys']
>>>
当下package中的class便都被加载了,可以正常初始化对象,不需要from xxx import yyy写很多行,也不需要每次新增文件再来添加。
__all__仅对from xxx import * 起作用。
__init__.py其中有一个__all__的变量,代表了当前package的子模块。我们可以修改__all__来达成动态加载所有模块的作用。
当我们需要import *时就能获取所有的文件模块时,可以在__init__.py中添加:
from os.path import dirname, basename, isfile, join
import glob
modules = glob.glob(join(dirname(__file__), "*.py"))
__all__ = [basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
print(__all__)
当我们import时,输出就会变成:
>>> from projects.byblog.pages import *
['common_component', 'post_detail_page', 'login_page', 'post_list_page', 'main_page']
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'common_component', 'login_page', 'main_page', 'post_detail_page', 'post_list_page']
>>> dir(login_page)
['CommonComponent', 'LoginPage', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
>>>
我们可以看到,dir时能看到目录下的文件已经被加载了,且能成功获取比如login_page里的class。其实这种跟from xxx import *很类似,只是使用场景不一样。