懒加载特性¶
在 v1.0.41 后,本项目提供了 懒加载 特性,可以将模块和模块内常用方法或类包装为 LazyModule 和 LazyCallable,只在实际访问相应模块或类/方法时,才会加载
该特性可以显著减少模块导入时间
使用场景¶
例如有如下目录结构的项目
其中 fake_module 内容如下
import pandas as pd
from deepfos.api.base import RootAPI
class F(RootAPI):
pass
从其 import 的内容可以看出,为了导入 Class F ,将先导入 pandas 和 deepfos.api.base
假如 main.py 中只有如下代码
from demo.fake_pkg.fake_module import F
profile耗时倒序统计
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 <frozen importlib._bootstrap>:1002(_find_and_load)
931/1 0.004 0.000 2.100 2.100 <frozen importlib._bootstrap>:967(_find_and_load_unlocked)
879/3 0.004 0.000 2.098 0.699 <frozen importlib._bootstrap>:659(_load_unlocked)
1187/3 0.001 0.000 2.098 0.699 <frozen importlib._bootstrap>:220(_call_with_frames_removed)
745/3 0.003 0.000 2.098 0.699 <frozen importlib._bootstrap_external>:844(exec_module)
1 0.000 0.000 2.095 2.095 ...\demo\fake_pkg\fake_module.py:1(<module>)
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(<module>)
829/480 0.001 0.000 0.709 0.001 <frozen importlib._bootstrap>:1033(_handle_fromlist)
924/788 0.010 0.000 0.636 0.001 <frozen importlib._bootstrap>:901(_find_spec)
745 0.009 0.000 0.508 0.001 <frozen importlib._bootstrap_external>:916(get_code)
1 0.000 0.000 0.380 0.380 ...\deepfos\api\base.py:1(<module>)
......
1 0.000 0.000 0.191 0.191 ...\venv\lib\site-packages\loguru\__init__.py:1(<module>)
1 0.000 0.000 0.175 0.175 ...\venv\lib\site-packages\loguru\_logger.py:1(<module>)
......
1 0.000 0.000 0.144 0.144 ...\venv\lib\site-packages\requests\__init__.py:8(<module>)
......
通过profile可得知,这些第三方包(包括deepfos)的导入耗时总和在1.5s以上,而代码本身并没有实际逻辑,导入的模块是未被使用的
而在引入了懒加载特性后,则可在 fake_pkg 的__init__.py文件中,使用 lazy_callable 方法,将 fake_module 下的 Class F 替换为懒加载的Class
from deepfos.lazy_import import lazy_callable
F = lazy_callable('demo.fake_pkg.fake_module', 'F')
main.py 作相应更新
from demo.fake_pkg import F
则上述 main.py 的profile将变为如下
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 <frozen importlib._bootstrap>:1002(_find_and_load)
293/1 0.001 0.000 0.522 0.522 <frozen importlib._bootstrap>:967(_find_and_load_unlocked)
362/2 0.000 0.000 0.521 0.261 <frozen importlib._bootstrap>:220(_call_with_frames_removed)
279/2 0.001 0.000 0.521 0.261 <frozen importlib._bootstrap>:659(_load_unlocked)
245/2 0.001 0.000 0.521 0.261 <frozen importlib._bootstrap_external>:844(exec_module)
1 0.000 0.000 0.521 0.521 ...\demo\fake_pkg\__init__.py:1(<module>)
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(<module>)
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(<module>)
1 0.000 0.000 0.146 0.146 ...\deepfos\lib\constant.py:1(<module>)
1 0.000 0.000 0.143 0.143 ...\venv\lib\site-packages\requests\__init__.py:8(<module>)
1 0.000 0.000 0.136 0.136 ...\deepfos\options.py:1(<module>)
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(<module>)
1 0.000 0.000 0.116 0.116 ...\venv\lib\site-packages\loguru\_logger.py:1(<module>)
......
虽然 deepfos.lazy_import 的引入增加了少许来自deepfos的__init__.py和 deepfos.lazy_import 自身需要导入的模块及相应逻辑,但省去了耗时最高的 pandas 包和 deepfos.api.base 模块的导入时间,使得总体耗时减少了1.577s
lazy_module¶
该方法将提供根据module名包装的 LazyModule实例 ,对该module的import操作(即 importlib.import )将在访问module内的成员时才发生。
>>> 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'
警告
预期lazy_module的module_name是模块名,而非包(package)名,这意味着,如果设置了lazy_module,
将其当作包来访问其下module的操作将导致 AttributeError
例如在demo的__init__.py中如下编写
from deepfos.lazy_import import lazy_module
fake_pkg = lazy_module('demo.fake_pkg')
试图在demo的同级目录访问 fake_pkg 下的 fake_module
from demo import fake_pkg
fake_pkg.fake_module
将报如下错
AttributeError: module 'demo.fake_pkg' has no attribute 'fake_module'
lazy_callable¶
相较于懒加载模块,更常见的实际使用场景是引入某模块内的方法/类时,希望避免对一整个模块的导入,因此基于 lazy_module 方法,提供了lazy_callable方法
该方法将module名对应的module包装为 LazyModule 后,为其增加与names(多个方法/类名)同名的 LazyCallable 成员,对其模块内方法/类的调用将变为对 LazyCallable 的调用
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文件的导入
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