目录
回顾
到此我们已经知道了python的面向对象支持方法以及如何组织我们的程序。
对于模块的理解现在也只是局限于 功能的封装,借助C的头文件帮助理解
对于包可以借助Java的包来帮助理解,还都很浅显,相信随着后面继续使用的深入,我们的理解会更加深刻
这也是我们 封装运用的基础,一定要好好掌握日后才可以自由的进行封装粒度的把控。
模块和包
包和模块的工作方式类似,但是内部的工作方式不同。
模块是一个py文件,封装着一些python功能
包是一个文件夹目录,里面含有一堆Py文件来共同完成一个大的目标
其中的控制就在 __init__.py 文件
一旦建立了包,那么模块就可以认为是包的属性了,
因此 就有了 from 包名 import 模块名 的使用了
模块是包的属性
模块是包的属性,因此可以使用 from 包名 import 模块名
这个和一般的从模块导入属性是类似的
比如我们之前写的 MyPack 模块,里面有classInfo.py 和 study.py 两个模块
我们如果直接进行这两个模块的导入看看有什么问题:
实验
我们从包的目录 D:\Apl\python_work\basic 进入到python 环境,此时直接导入包那么肯定会报错
因为我们的__init__.py文件中导入了 两个模块,导入模块一定要该模块的路径能被搜寻到
因此模块的路径必须要在 sys.path中
导入包报错
将__init__.py中导入的模块路径加入sys.path
现在模块的路径已经在 sys.path中了,按道理我们是不是可以直接导入模块了
我们发现是可以导入的,但是 我们之前的确是先导入包了,__init__.py 文件被导入了会不会有影响
我们另外打开一个dos窗口,先加入模块路径,但是不导入包,直接导入模块看看啥情况
也是可以导入的
from 包 import 模块
模块属性访问控制
模块中的共有项目和私有项目
那么什么是公有的呢?
1.如果模块定义了__all__列表,那么解释器使用__all__列表里面的内容确定哪些是公有项
2.如果没有定义__all__,那么解释器导入除了以下划线_开头的项目之外的所有项目
如 clac是公有的,但是 _calc不是
私有被隐藏了还是不能访问
java中是否被隐藏是通过访问控制进行的,私有的方法外部是点不出来的。
而且该类暴露给外部的api是看不到私有的属性的
那么python呢? help 查看模块是看不到私有属性的api,但是我们自己如果看代码知道属性的话可以使用
也就是语法上并没有强制我们不能使用,仅仅是作为旁观者不知道有这个属性不能使用
如果说真正被隐藏也只针对 import * 这一种导入方式有影响
我们按照上面说的命名规则 下划线 来看看:
模块的FUNCTIONS 描述 - help 函数
我们可以使用
help(模块) 来查看一个模块内部的功能属性
这个函数结果中的FUNCTIONS 描述就是我们暴露被外部的api
下滑线命名对模块导入可见性
如下面的moduleTest.py 该模块定义了三个函数
其中只有这个 display函数不是下划线开头命名的
使用 import 模块 看不了私有属性但是可以访问
我们导入这个模块看看它有什么输出:
我们发现只有这个 display 被输出了,其他两个函数是没有被输出的
然后我们使用这个模块里私有的属性
from 模块 import 私有属性 可以导入可以使用
上面我们是import 的模块 import 模块 之后 是不影响我们使用 私有属性的,仅仅是api看不到而已
现在我们 直接 使用 from 模块 import 私有属性看好看
from 模块 import * 无法访问私有属性
下面直接导入 moduleTest 中的 所有元素,发现 _开头的是无法导入进来的
下滑线命名总结
通过上面的实验我们知道:只有 from 模块 import * 的时候 , 下划线的属性是无法导入到当前的命名空间中的
但是 import 模块 和 from 模块 import 属性 这两种方式都是可以访问的.
那么为什么 import * 不能呢?
from ... import *
*的意义
其中 的 * 表示 告诉python 解释器导入模块的全部 公有方法,因此 只有满足模块中公有项目的定义表才可以被导入
如果使用 from 包名 import * 时候,这个*可不是都可以导出的,需要显示指定希望得的名称
出现在__all__列表中的所有名称也仅仅是这些名称可通过 * 导出
或者是下划线以外命名的情况
__all__列表指定的访问控制
借助模块文件的__all__列表 来指定这个模块中可以被导出的属性
我们将之前的 moduleTest.py 文件修改,加入 __all__指定
__all__列表指定和下划线命名规则不会结合
有__all__列表存在,按照__all__列表指定控制公有私有,此时的下划线命名规则失效
我们先 将原有的私有属性 _action 给加入到 __all__列表中
但是之前 符合公有的 display 不加入,我们查看一下模块的 function
发现 display 没有出现,如果有__all__列表 那么完全按照 __all__列表的指定来控制
同时多出来一个 DATA 的说明项目
__all__列表对模块导入可见性
通过上面下划线的实验呢我们知道 私有项目只对这个 import * 有影响
对于其他两种的没有影响.
那么 __all__列表一样,也只对 import * 产生影响
我们再将 display 也加入到 __all__列表,然后使用 import * 就可以访问这个 display了
包属性访问控制
前面我们看到了模块属性访问控制,公有和私有 对于 导入可见性
私有项目只有 import * 是不可见的.
那么包下面的属性如何控制呢?
模块就是包的属性, 包通过 __init__.py 来管理 模块的
__init__.py 本身就是一个模块文件
因此可以使用 __init__.py 里面的 __all__列表来进行管理的
对于包而言,模块本身就是包的属性
将模块的属性导入到包空间中
模块里面的属性我们可以通过 from 模块 import 属性 将模块的属性导入到包的空间中
那么包的属性可见性 就是包空间中的属性可见性访问控制
包的空间属性控制就是通过__init__.py来管理
由于 __init__.py本身就是模块文件,因此可以理解为是 __init__.py 属性访问可见性
这个就和之前的模块一样了
实验
我们用之前的 MyPack 模块来看下
首先在classInfo 模块中加入一个 以下划线开头的类_innerInfo 作为下滑线命名的属性
MyPack 包下面的 __init__.py文件中先不用__all__列表
但是我们要将 包下面模块的属性导入到 包空间中,因此 __init__.py 中的导入要修改下
包空间-导入模块的属性-下划线命名
根据__init__.py充当 管理模块的概念我们知道 包空间中的 下划线开头的名字属性
被认为是 私有的无法通过 import * 导入到当前空间中
这个和模块的导入可见性是一致的
包空间-导入模块的属性 __all__列表使用
我们修改下 __init__.py 文件 增加 __all__列表
from 包 import * 总结
通过对比我们发现,只有 import * 才有真正上私有的可见性问题
包的属性要通过 __init__.py 文件来进行管理,在该文件中 要把模块的属性导入到包的空间中
这样包空间就对这些模块的属性起了约束作用。
实际可用理解为 是 __init__.py 这个管理模块 起作用了
from 包 import * 最终 将包空间中的属性导入到了 当前的空间中
和 from 模块 import * 不同的就是 包空间要先将 模块属性导入到包空间中
模块属性------> __init__.py -------> 当前空间
这样我们就可以直接在当前空间中访问 包下面模块中的属性了
通过 包.模块.属性访问
前面我们讨论的可见性都是 对于 import * 这种而言的
但是我们 如果不使用这种方式 导入 使用限定名称访问是没有问题
包括 包也一样
可以通过 包.模块.属性 的方式来进行访问的
sys.modules 字典
sys.modules 字典包含了目前已经导入的全部模块
sys.modules.keys 返回的列表就是已经加载过的每个模块的名称
区分当前作用域
模块 ,包 ,以及当前运行空间组成了不同的作用域
对于 属性的导入 的可见性我们要知道当前作用域是啥
模块的__name__属性
__name__属性可以告诉你 正在运行作用域的名称
比如 在模块中 检查 __name__属性的时候,返回的字符串就是这个模块名
经常可以在 模块中看到 if __name__ =='__main__'
__name__属性指明了 当前模块的属性名称
还有 如果我们在模块最后一行 写上 上面代码 表明 上面的模块代码可以通过执行的
将模块作为程序运行
模块本身就是一个py文件,当然可以在里面写一些可执行的代码
但是python给模块定义了一个约定,当一个模块自己运行时它应该执行模块测试
因此如果当前是在运行模块
if __name__== '__main__'
tset()
这种情况需要调用模块的测试
模块的测试
可以针对一个模块文件写一个测试模块文件
或者在原先的模块文件中 定义 test函数来进行测试
安装模块
python解释器在sys.path变量中列出的目录进行模块的查找
sys.path 包含当前目录,所以总是可以使用当前路径中的模块
但是如果我们想要在多个系统中使用模块的时候,需要将模块安装到sys.path变量列出的
某个目录下
大多数情况下,需要将python模块放到 site-packages目录中
除了模块还可以创建模块的包
包是一个安装到相同目录结构的相关模块的集合