懒加载特性 ============ 在 v1.0.41 后,本项目提供了 **懒加载** 特性,可以将模块和模块内常用方法或类包装为 **LazyModule** 和 **LazyCallable**,只在实际访问相应模块或类/方法时,才会加载 该特性可以显著减少模块导入时间 使用场景 ------------- 例如有如下目录结构的项目 .. panels:: :container: container pb-1 img-auto-width :column: col-lg-12 p-0 :body: p-0 .. image:: ../images/lazy_import_demo.png 其中 *fake_module* 内容如下 .. code-block:: python import pandas as pd from deepfos.api.base import RootAPI class F(RootAPI): pass 从其 *import* 的内容可以看出,为了导入 **Class F** ,将先导入 *pandas* 和 *deepfos.api.base* 假如 main.py 中只有如下代码 .. code-block:: python from demo.fake_pkg.fake_module import F profile耗时倒序统计 .. code-block:: :emphasize-lines: 12, 14, 18 1242642 function calls (1223393 primitive calls) in 2.099 seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function) 754/1 0.003 0.000 2.100 2.100 {built-in method builtins.exec} 935/1 0.006 0.000 2.100 2.100 :1002(_find_and_load) 931/1 0.004 0.000 2.100 2.100 :967(_find_and_load_unlocked) 879/3 0.004 0.000 2.098 0.699 :659(_load_unlocked) 1187/3 0.001 0.000 2.098 0.699 :220(_call_with_frames_removed) 745/3 0.003 0.000 2.098 0.699 :844(exec_module) 1 0.000 0.000 2.095 2.095 ...\demo\fake_pkg\fake_module.py:1() 530/76 0.001 0.000 1.497 0.020 {built-in method builtins.__import__} 1 0.000 0.000 1.087 1.087 ...\venv\lib\site-packages\pandas\__init__.py:3() 829/480 0.001 0.000 0.709 0.001 :1033(_handle_fromlist) 924/788 0.010 0.000 0.636 0.001 :901(_find_spec) 745 0.009 0.000 0.508 0.001 :916(get_code) 1 0.000 0.000 0.380 0.380 ...\deepfos\api\base.py:1() ...... 1 0.000 0.000 0.191 0.191 ...\venv\lib\site-packages\loguru\__init__.py:1() 1 0.000 0.000 0.175 0.175 ...\venv\lib\site-packages\loguru\_logger.py:1() ...... 1 0.000 0.000 0.144 0.144 ...\venv\lib\site-packages\requests\__init__.py:8() ...... 通过profile可得知,这些第三方包(包括deepfos)的导入耗时总和在1.5s以上,而代码本身并没有实际逻辑,导入的模块是未被使用的 而在引入了懒加载特性后,则可在 *fake_pkg* 的__init__.py文件中,使用 lazy_callable_ 方法,将 *fake_module* 下的 **Class F** 替换为懒加载的Class .. code-block:: python from deepfos.lazy_import import lazy_callable F = lazy_callable('demo.fake_pkg.fake_module', 'F') main.py 作相应更新 .. code-block:: python from demo.fake_pkg import F 则上述 main.py 的profile将变为如下 .. code-block:: :emphasize-lines: 12, 14, 19 295750 function calls (289263 primitive calls) in 0.522 seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function) 247/1 0.001 0.000 0.522 0.522 {built-in method builtins.exec} 294/1 0.002 0.000 0.522 0.522 :1002(_find_and_load) 293/1 0.001 0.000 0.522 0.522 :967(_find_and_load_unlocked) 362/2 0.000 0.000 0.521 0.261 :220(_call_with_frames_removed) 279/2 0.001 0.000 0.521 0.261 :659(_load_unlocked) 245/2 0.001 0.000 0.521 0.261 :844(exec_module) 1 0.000 0.000 0.521 0.521 ...\demo\fake_pkg\__init__.py:1() 67/4 0.000 0.000 0.518 0.130 {built-in method builtins.__import__} 1 0.000 0.000 0.372 0.372 ...\deepfos\__init__.py:2() 1 0.000 0.000 0.235 0.235 ...\deepfos\_version.py:580(get_versions) 1 0.000 0.000 0.235 0.235 ...\deepfos\_version.py:222(git_pieces_from_vcs) 5 0.000 0.000 0.234 0.047 ...\deepfos\_version.py:71(run_command) 5 0.000 0.000 0.150 0.030 ...\Python39\lib\subprocess.py:1090(communicate) 1 0.000 0.000 0.148 0.148 ...\deepfos\lazy_import.py:1() 1 0.000 0.000 0.146 0.146 ...\deepfos\lib\constant.py:1() 1 0.000 0.000 0.143 0.143 ...\venv\lib\site-packages\requests\__init__.py:8() 1 0.000 0.000 0.136 0.136 ...\deepfos\options.py:1() 249 0.131 0.001 0.131 0.001 {method 'read' of '_io.BufferedReader' objects} 1 0.000 0.000 0.125 0.125 ...\venv\lib\site-packages\loguru\__init__.py:1() 1 0.000 0.000 0.116 0.116 ...\venv\lib\site-packages\loguru\_logger.py:1() ...... 虽然 *deepfos.lazy_import* 的引入增加了少许来自deepfos的__init__.py和 *deepfos.lazy_import* 自身需要导入的模块及相应逻辑,但省去了耗时最高的 *pandas* 包和 *deepfos.api.base* 模块的导入时间,使得总体耗时减少了1.577s lazy_module -------------------- 该方法将提供根据module名包装的 **LazyModule实例** ,对该module的import操作(即 *importlib.import* )将在访问module内的成员时才发生。 .. code-block:: python >>> import sys >>> from deepfos.lazy_import import lazy_module >>> ccc = lazy_module("aaa.bbb.ccc") # 当前命名空间可访问到的ccc等效于 # from aaa.bbb import ccc中的ccc # 此时,aaa.bbb.ccc不在sys.modules中 >>> 'aaa.bbb.ccc' in sys.modules False # 在试图访问ccc模块内的D成员时 # 才会试图导入aaa, aaa.bbb, aaa.bbb.ccc # 由于aaa不存在 # 此处将报ModuleNotFoundError >>> print(ccc.D) Traceback (most recent call last): ...... ModuleNotFoundError: No module named 'aaa' .. warning:: 预期lazy_module的module_name是模块名,而非包(package)名,这意味着,如果设置了lazy_module, 将其当作包来访问其下module的操作将导致 :class:`AttributeError` 例如在demo的__init__.py中如下编写 .. code-block:: python from deepfos.lazy_import import lazy_module fake_pkg = lazy_module('demo.fake_pkg') 试图在demo的同级目录访问 *fake_pkg* 下的 *fake_module* .. code-block:: python from demo import fake_pkg fake_pkg.fake_module 将报如下错 .. code-block:: AttributeError: module 'demo.fake_pkg' has no attribute 'fake_module' lazy_callable -------------------- 相较于懒加载模块,更常见的实际使用场景是引入某模块内的方法/类时,希望避免对一整个模块的导入,因此基于 lazy_module_ 方法,提供了lazy_callable方法 该方法将module名对应的module包装为 **LazyModule** 后,为其增加与names(多个方法/类名)同名的 **LazyCallable** 成员,对其模块内方法/类的调用将变为对 **LazyCallable** 的调用 .. code-block:: python from deepfos.lazy_import import lazy_callable # lazy_callable返回的是LazyCallable的tuple A, = lazy_callable("aaa.bbb.ccc", "A") # 指定多个LazyCallable B, C, D, E = lazy_callable("aaa.bbb.ccc", "B", "C", "D", "E") Deepfos项目的懒加载模块和方法/类 --------------------------- 目前本项目中的 *core* , *db* , *element* 包都已支持懒加载特性,对其中的懒加载方法/类,可以直接从 *core* , *db* , *element* 包导入,得到的即为懒加载后的 **LazyCallable** 对象,而不涉及其所在module文件的导入 .. code-block:: python from deepfos.element import Datatable # 此处的Datatable是LazyCallable包装后的Datatable ele = Datatable('test') deepfos.core ~~~~~~~~~~~~~~~~~~~~~~~~ - cube - SysCube - Cube - as_function_node - dimension - DimMember - SysDimension - read_expr - Dimension - DimExprAnalysor - ElementDimension - logictable - SQLCondition - BaseTable - MetaTable - TreeRenderer deepfos.db ~~~~~~~~~~~~~~~~~~~~~~~~ - mysql - MySQLClient - AsyncMySQLClient - clickhouse - ClickHouseClient - AsyncClickHouseClient - oracle - OracleClient - AsyncOracleClient - OracleDFSQLConvertor - sqlserver - SQLServerClient - AsyncSQLServerClient - kingbase - KingBaseClient - AsyncKingBaseClient - gauss - GaussClient - AsyncGaussClient - dameng - DaMengClient - AsyncDaMengClient - postgresql - PostgreSQLClient - AsyncPostgreSQLClient - deepengine - DeepEngineClient - AsyncDeepEngineClient deepfos.element ~~~~~~~~~~~~~~~~~~~~~~~~ - accounting - AccountingEngines - BillEngines - CallbackInfo - apvlprocess - ApprovalProcess - AsyncApprovalProcess - bizmodel - AsyncBusinessModel - BusinessModel - CopyConfig - datatable - AsyncDataTableMySQL - AsyncDataTableClickHouse - AsyncDataTableOracle - AsyncDataTableSQLServer - AsyncDataTableKingBase - AsyncDataTableGauss - AsyncDataTableDaMeng - AsyncDataTablePostgreSQL - AsyncDataTableDeepEngine - Datatable - DataTableMySQL - DataTableClickHouse - DataTableOracle - DataTableSQLServer - DataTableKingBase - DataTableGauss - DataTableDaMeng - DataTablePostgreSQL - DataTableDeepEngine - dimension - AsyncDimension - Dimension - Strategy - fact_table - AsyncFactTable - FactTable - finmodel - AsyncFinancialCube - FinancialCube - journal_template - AsyncJournalTemplate - JournalTemplate - FullPostingParameter - rolestrategy - AsyncRoleStrategy - RoleStrategy - smartlist - AsyncSmartList - SmartList - variable - AsyncVariable - Variable