Bob's Blog

Web开发、测试框架、自动化平台、APP开发、机器学习等

返回上页首页

python动态加载所有class或所有module



如下场景:在一个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 *很类似,只是使用场景不一样。

下一篇:  Gitlab CI中限制pipeline同时运行
上一篇:  Django restframework加Vue打造前后端分离的网站(十五)多重模板

共有0条评论

添加评论

暂无评论