最近看了看importlib库的的源码,先插个眼,后续有新理解再继续更新
模块的加载:
先找到模块描述 spec_from_file_location
再将描述初始化成一个module类 module_from_spec
最后放入python的exec()中 spec(模块声明).loader.exec_module(module(根据模块声明生成的模块类))
即完成了对某个模块的加载
#__all__ = ['__import__', 'import_module', 'invalidate_caches', 'reload']
# 先插个眼,后续加深对该部分代码的理解
# =================================__import__方法==================================
# 方法实现代码如下,返回字符串形式的模块名
"""
def __import__(name, globals=None, locals=None, fromlist=(), level=0):
if level == 0:
# 阅读源码发现该方法根据传入的模块名,会使用sys.module的方法去解释器中查询该模块是否存在
module = _gcd_import(name)
else:
globals_ = globals if globals is not None else {}
package = _calc___package__(globals_)
module = _gcd_import(name, package, level)
if not fromlist:
if level == 0:
return _gcd_import(name.partition('.')[0])
elif not name:
return module
else:
cut_off = len(name) - len(name.partition('.')[0])
return sys.modules[module.__name__[:len(module.__name__)-cut_off]]
elif hasattr(module, '__path__'):
return _handle_fromlist(module, fromlist, _gcd_import)
else:
return module
"""
# =====================================import_module方法===================================
# 与__import__方法类似,都是通过sys.module方法查询解释器中有无该方法
"""
def import_module(name, package=None):
level = 0
if name.startswith('.'):
if not package:
msg = ("the 'package' argument is required to perform a relative "
"import for {!r}")
raise TypeError(msg.format(name))
for character in name:
if character != '.':
break
level += 1
return _bootstrap._gcd_import(name[level:], package, level)
"""
# ========================================invalidate_caches方法==========================================
# 用于清除查找器缓存
import importlib
"""
def invalidate_caches():
for finder in sys.meta_path:
if hasattr(finder, 'invalidate_caches'):
finder.invalidate_caches()
"""
# ==========================================reload方法==============================
# 将方法塞入sys.module中并执行
# =======================_gcd_import方法=============================
"""
def _gcd_import(name, package=None, level=0):
_sanity_check(name, package, level) # 检查包命名规范
if level > 0:
name = _resolve_name(name, package, level)
return _find_and_load(name, _gcd_import)
"""
# 判断模块式否在解释器中运行如果不在
"""
def _find_and_load(name, import_):
with _ModuleLockManager(name):
module = sys.modules.get(name, _NEEDS_LOADING)
if module is _NEEDS_LOADING:
return _find_and_load_unlocked(name, import_)
if module is None:
message = ('import of {} halted; '
'None in sys.modules'.format(name))
raise ModuleNotFoundError(message, name=name)
_lock_unlock_module(name)
return module
"""
# 此方法主要涉及两个方法,可实现跨域调模块
# spec_from_file_location(module,"path")/ module_from_spec(spec) / spec.loader.exec_module(module)
# 本人另一篇博客有相应方法的应用 https://blog.csdn.net/weixin_44534915/article/details/123003919
"""
def _find_and_load_unlocked(name, import_):
path = None
parent = name.rpartition('.')[0]
if parent:
if parent not in sys.modules:
_call_with_frames_removed(import_, parent)
# Crazy side-effects!
if name in sys.modules:
return sys.modules[name]
parent_module = sys.modules[parent]
try:
path = parent_module.__path__
except AttributeError:
msg = (_ERR_MSG + '; {!r} is not a package').format(name, parent)
raise ModuleNotFoundError(msg, name=name) from None
spec = _find_spec(name, path) # 该方法会根据路径找到存在的模块并返回模块说明,具体方法主要是spec_from_file_location方法
if spec is None:
raise ModuleNotFoundError(_ERR_MSG.format(name), name=name)
else:
module = _load_unlocked(spec) # 加载模块声明,module_from_spec主要是这个方法,并用spec.loader.exec_module(module)写入解释器
if parent:
# Set the module as an attribute on its parent.
parent_module = sys.modules[parent]
child = name.rpartition('.')[2]
try:
setattr(parent_module, child, module)
except AttributeError:
msg = f"Cannot set an attribute on {parent!r} for child module {child!r}"
_warnings.warn(msg, ImportWarning)
return module
"""
# ==================================关于spec_from_file_location模块声明方法 ===================================
"""
def spec_from_file_location(name, location=None, *, loader=None,
submodule_search_locations=_POPULATE):
if location is None:
location = '<unknown>'
if hasattr(loader, 'get_filename'):
try:
location = loader.get_filename(name)
except ImportError:
pass
else:
location = _os.fspath(location) # 根据传入的路径寻找模块找到对应文件
spec = _bootstrap.ModuleSpec(name, loader, origin=location)
spec._set_fileattr = True
if loader is None:
for loader_class, suffixes in _get_supported_file_loaders():
if location.endswith(tuple(suffixes)):
loader = loader_class(name, location)
spec.loader = loader
break
else:
return None
if submodule_search_locations is _POPULATE:
if hasattr(loader, 'is_package'):
try:
is_package = loader.is_package(name)
except ImportError:
pass
else:
if is_package:
spec.submodule_search_locations = []
else:
spec.submodule_search_locations = submodule_search_locations
if spec.submodule_search_locations == []:
if location:
dirname = _path_split(location)[0]
spec.submodule_search_locations.append(dirname)
return spec
"""
# ============================module_from_spec根据模块描述生成模块类 ==============================================
"""
def module_from_spec(spec):
# Typically loaders will not implement create_module().
module = None
if hasattr(spec.loader, 'create_module'):
# If create_module() returns `None` then it means default
# module creation should be used.
module = spec.loader.create_module(spec)
elif hasattr(spec.loader, 'exec_module'):
raise ImportError('loaders that define exec_module() '
'must also define create_module()')
if module is None:
module = _new_module(spec.name)
_init_module_attrs(spec, module) # 初始化一个module类
return module
"""
# =========================================spec(模块声明).loader.exec_module(module(根据模块声明生成的模块类))
"""
module_desc.loader.exec_module(moudle_spec) #最终是执行的pthon内置方法exec()
"""