#秉着python一切皆为对象的原则,我来看看python库的类型 import os print(type(os))# <class 'module'> 结果为一个名为'module'的类型 #1、什么样的文件类型叫做模块 #以.py文件结尾的都可以被python认为是模块 #2、package的概念 #为了帮助组织模块并提供名称层次结构,Python 还引入了包的概念 #通常以一个包含 __init__.py 文件的目录形式实现。 当一个正规包被导入时, #这个 __init__.py 文件会隐式地被执行,它所定义的对象会被绑定到该包命名空间中的名称。__init__.py #文件可以包含与任何其他模块中所包含的 Python 代码相似的代码,Python 将在模块被导入时为其添加额外的属性。 #值得注意的是,当模块被正确加载的时候模块会被解析成module对象,所谓的添加额外属性也就是在类型的属性命名空间 #__dict__添加,python中很多对象属性的管理都是动态添加的 #要注意的一个重点概念是所有包都是模块,但并非所有模块都是包。或者换句话说,包只是一种特殊的模块。 #特别地,任何具有 __path__ 属性的模块都会被当作是包。 import logging #我们拿logging来举例,准确来讲logging是一个包,它的目录下包含了多个模块,按照上述,包可以看做一种特殊的模块 print(logging.__loader__.is_package('logging')) #通过包加载器来判断是否为一个包,当然我们可以通过模块的特殊属性来判断 print(logging.__name__) #限定名称如果为顶层包,则直接反馈包名,否则将以'.'的形式呈现路径结构 from logging import config print(config.__name__) #logging.config print(logging.__package__)#logging print(logging.__file__)#D:python3liblogging\__init__.py 如果为包则反馈包下__init__.py文件的路径,此文件会在包被导入时执行 print(config.__file__) #模块的文件路径 print(config.__path__) #报错了,模块没有__path__属性,而包则必须包含此属性,因此我们可以通过判断此属性来却别模块与包 '__path__' in config.__dict__ ? print(logging.__path__) #3、导入相关的模块属性 #__name__¶ #__name__ 属性必须被设为模块的完整限定名称。 此名称被用来在导入系统中唯一地标识模块。 #fullname 限定名称的定义 #参照官方文档的定义: 一个以点号分隔的名称,显示从模块的全局作用域到该模块中定义的某个类、函数或方法的“路径” #__loader__ #__loader__ 属性必须被设为导入系统在加载模块时使用的加载器对象 #__package__ #模块的 __package__ 属性必须设定。 其取值必须为一个字符串,但可以与 __name__ 取相同的值。当模块是包时,其__package__ 值应该设为其 __name__ 值。 #当模块不是包时,对于最高层级模块 __package__ 应该设为空字符串,对于子模块则应该设为其父包名 #__spec__ #__spec__ 属性必须设为在导入模块时要使用的模块规格说明。 对 __spec__ 的正确设定将同时作用于 解释器启动期间初始化的模块 #__path__ #如果模块为包(不论是正规包还是命名空间包),则必须设置模块对象的 __path__ 属性 #不是包的模块不应该具有 __path__ 属性。 #__file__ #包或模块的文件路径,当为包是路径为__init__.py文件的路径,当为模块时,则为模块的路径 #4、模块加载过程 #官方提供的加载过程实现源码 ''' module = None if spec.loader is not None and hasattr(spec.loader, 'create_module'): # It is assumed 'exec_module' will also be defined on the loader. module = spec.loader.create_module(spec) if module is None: module = ModuleType(spec.name) #这里我可以通过types模块来引入ModuleType来创建模块,但是通常不这样做 #通过会根据spec来创建模块,因为这样可以在模块上设置更多的特有属性 #可以通过importlib.util.module_from_spec() 来实现 # The import-related module attributes get set here: _init_module_attrs(spec, module) if spec.loader is None: if spec.submodule_search_locations is not None: # namespace package sys.modules[spec.name] = module else: # unsupported raise ImportError elif not hasattr(spec.loader, 'exec_module'): module = spec.loader.load_module(spec.name) # Set __loader__ and __package__ if missing. else: sys.modules[spec.name] = module try: spec.loader.exec_module(module) except BaseException: try: del sys.modules[spec.name] except KeyError: pass raise return sys.modules[spec.name] ''' #过程说明 ''' 1、如果在 sys.modules 中存在指定名称的模块对象,导入操作会已经将其返回。 2、加载器执行模块代码之前,该模块将存在于 sys.modules 中。 这一点很关键,因为该模块代码可能(直接或间接地)导入其自身; 预先将其添加到 sys.modules 可防止在最坏情况下的无限递归和最好情况下的多次加载。 3、如果加载失败,则该模块 -- 只限加载失败的模块 -- 将从 sys.modules 中移除。 任何已存在于 sys.modules 缓存的模块, 以及任何作为附带影响被成功加载的模块仍会保留在缓存中。 这与重新加载不同,后者会把即使加载失败的模块也保留在 sys.modules 中。 4、在模块创建完成但还未执行之前,导入机制会设置导入相关模块属性(在上面的示例伪代码中为 “_init_module_attrs”),详情参见 后续部分。 5、模块执行是加载的关键时刻,在此期间将填充模块的命名空间(也就是module.__dict__)。 执行会完全委托给加载器,由加载器决定要填充的内容和方式。 6、在加载过程中创建并传递给 exec_module() 的模块并不一定就是在导入结束时返回的模块 [2]。 '''