chapter10.3模块化

模块化

一般来说,编程语言中,库,包,模块是同一种概念,是代码组织方式

python只有模块的对象类型,但是为了模块化组织模块的便利,提供了包的概念

模块module,指的是Python的源代码文件。

包package,指的是模块组织在一起的包名同名的目录及其相关文件

 

导入语句

import模块,允许写成一行,完全导入

import ... as ...         模块别名

  impor语句,会找到指定的模块,加载和初始化它,生成模块对象,找不到,抛出ImportError异常

  在Import所在的作用域的局部命名空间中,增加和上一步创建的对象关联

  import后只能出现模块类型,包或者模块

总结:

import语句,只导入顶级模块,其名称会加入到本地名词空间,并绑定到其模块对象

导入非顶级模块,只将其顶级模块的名称加入本地命名空间,导入的模块必须使用完全限定名称来访问。

如果使用了as,as后的名称直接绑定到导入的模块对象,该名称加入本地名词空间

from 模块 import  后跟较多,模块,类,函数,变量都可以

from ... import ... as  ... 

from后的模块不会填入名词空间,

from pathlib import Path, PosixPath     #在当前名词空间导入该模块指定的成员
print(dir())    #[..., 'Path','PosixPath']
from functools import *     #在当前名词空间导入该模块所有的公共成员或指定成员
print(dir())    ##'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES',..., 'update_wrapper', 'wraps'
from os.path import exists  #加载,初始化os,os.path模块,exists加入本地名词空间

if exists('test.txt'):
    print('exists')
else:
    print('not found')

print(dir())
print(exists)

import os

print(os.path.exists)
print(exists)
print(os.path.__dict__['exists'])
print(getattr(os.path,'exists'))
#以上方法获得同一个对象

找到from子句中指定的模块加载并初始化它(注意不是导入)

对于import子句后的名称

  先查from子句导入的模块是否具有该名称的属性

  如果没有,则尝试导入该模块是否具有该名称的属性

  还没有找到,就抛出ImportError异常

  这个名称保存到本地名词空间,如果有as子句,则使用as子句后的名称

导入的模块不会消亡,只是没有标识符记得

导入的名词对象在创建后不会改变,只创建一次,重复导入或者重命名后,也不会新建对象

自定义模块

.py就是模块天然的模块

命名规则

  模块就是文件名

  必须符合标识符要求,test-module.py  不能作为文件名

  不要使用系统模块名来命名,除非明确知道模块的用途

  通常模块名未全小写,下划线来分割

模块搜素顺序

sys.modules   显示当前模块内已经导入的模块

sys.path   查看搜索顺序,python模块的路径搜索顺序

import sys

for p in sys.path:
    print(p)

当加载模块时,需要从这个搜索路径中从前到后依次查找,并不会搜索这些目录的子目录

搜索到模块就加载,搜索不到就抛异常

路径也可以是字典,zip文件,egg文件。

  .egg文件,由setuptools库创建的包,第三方库常用的格式,添加了元数据(版本号,依赖信息等)信息的zip文件

路径顺序为

  程序主目录,程序运行的主程序的脚本所在的目录。

  PYTHONPATH目录,环境变量PYTHONPATH设置的目录也是搜索模块的路径。

  标准库目录,Python自带的库模块所在的目录

模块不会重复导入

模块不会重复导入,sys.modules储存所有已经加载过的所有模块,是一个字典

模块运行

__name__ 每个模块都有保存模块名,每个模块都有一个__name__特殊变量来储存当前模块名,如果不指定,则默认为源代码文件名,如果是包则有限定名

解释器初始化时,会初始化sys.module字典(保存已加载的模块),加载builtins(全局函数,常量)模块、__main__模块、sys模块,以及初始化模块搜索路径sys.path

当从标准输入(命令行方式输入),脚本(*.py),或交互式读取的时候,会将模块的__name__设置为__main__,模块的顶层代码就在__main__这个作用域中执行。顶层代码:模块中缩进最外层的代码

如果是import导入,则__name__默认就是模块名

脚本语言,可以直接运行,但有列外

if __name__ =='__main__' : 用途

本模块的功能测试,对于非本模块不会执行这个语句

避免主模块变更的副作用

顶层代码没有封装,主模块使用正常,但是一旦变更主模块,原来的变成了导入模块,由于原来代码没有封装,就会一起执行。

可以在该语句下放例子,测试代码

模块属性

__file__  字符串,源文件路径

__cached__  字符串,编译后的字节码文件路径

__spec__  显示模块的规范

__name__  模块名

__package__  当模块是包,同__name__;否则,可以设置为顶级模块的空字符串

目录可以当作包来看,包是特殊的模块。

目录不可以写入数据,__init__,包的特殊文件,初始化目录,目录的代码就在这个文件中

目录含有__init__就叫做包  package

子模块

包目录下的py文件,子目录都是其子模块

包可以没有__init__.py  不会影响导入,但是最好少做,尽量保留该文件

包可以更好的组织模块,尤其是大模块,可以拆分成很多子模块,便于使用某些功能就加载相应的模块

包目录中的__init__.py 是包第一次导入时执行的,内容可以是空,也可以是用于该包初始化工作的代码,最好不要删除它(低版本不可删除)

包目录间只能使用.作为分隔符,表示模块及子模块的层级关系

模块就是命名空间,其内部的顶层标识符,都是它的属性,可以通过__dict__或dir(module)查看

包也是模块,但模块不一定是包,包是特殊的模块,是一种组织方式,它包含__path__属性

dir只能到模块的边界,就是模块的界限

模块的边界非常强,模块下的文件都受模块管理,相当于国界

作用域,globals,模块是最大范围,locals显示当前作用域

locals() 和dir()不带参数时意义相同

绝对导入、相对导入

绝对导入,在import语句或者from导入模块,模块名称最前面不是以.点开头的

绝对导入总是去模块搜索路径中找,会先看是否已经加载该模块

相对导入,只能在包内使用,且只能在from后,import语法不支持

使用.表示当前目录内,

..表示上一级

使用了相对导入的模块不能作为主模块运行

一般用在包内部,相对导入只是为了内部相互引用资源的,不是为了直接运行,对于包来说,正确的使用方式是在顶级模块中使用这些包

访问控制

像_或者__开头的文件,都可以导入,模块内没有访问控制,所有变量都不做特殊处理

隐藏名对模块无用

私有属性在类下有用,在模块下无用

from ... import *  和 __all__

使用*时会将公共模块放入名词空间,但是如果在模块内定义了__all__,就可以控制给*访问的模块

__all__时一个列表,元素是字符串,每一个元素都是一个模块内的变量名

__all__只影响*号导入

总结:

使用from xyz import * 导入

1、如果模块没有__all__,只导入没有下划线开头的该模块下的变量。如果时包,子模块也不会导入,除非在__all__中设置,或__init__.py中导入

2、如果模块有__all__,只导入__all__列表中指定的名称,哪怕这个名词是下划线开头的或是子模块

3、from...import *    导入简单,但是会大量带入不使用的变量,甚至有可能造成名称的冲突,而__all__可以控制被导入模块在这种导入方式下提供想提供的变量名称。

编写时,应该尽量加入__all__

使用from module import name1,name2 导入

这种方式导入是明确的,哪怕是导入子模块,或者导入下划线开头的名字

模块对象是同一个,模块变量也是同一个,对模块变量的修改会影响所有使用者,

除非万不得已,或者明确知道自己在做什么,否则不要轻易修改模块的变量

打补丁,也可以修改模块的变量,类,函数等内容

猜你喜欢

转载自www.cnblogs.com/rprp789/p/9709147.html