一般来说,编程语言中,库,包,模块是同一种概念,是代码组织方式
python中只有一种模块对象类型,但是为了模块化组织的便利,提供了一个概念: 包
模块(module):指的是python的源代码文件
包(package):指的是模块组织在一起放入和包名同名的目录及相关文件
可以将代码量较大的程序分割成多个有组织,彼此间独立但又能互相交互的代码片段,这些自我包含的有组织的代码段就是模块
模块在物理形式上表现为以.py 结尾的代码文件
一个文件被看做一个独立的模块,一个模块也可以被看做是一个文件
模块的文件名就是模块的名字加上扩展名.py
每个模块都有自己的名称空间
Python 允许“导入”其他模块以实现代码重用,从而也实现了将独立的代码文件组织成更大的程序系统
Python 中,模块也是对象
在一个模块的顶层定义(全局变量)的所有变量都在被导入时成为了被导入模块的属性
一个Python程序通常包括一个顶层文件和其他的模块文件(0个,1个或多个)
顶层文件:包含了程序的主要控制流程
模块文件:为顶层文件或其他模块提供各种功能性组件
模块首次导入(或重载)时,Python会立即执行模块文件的顶层程序代码(不在函数内的代码),而位于函数体内的代码直到函数被调用后才会执行
Python自带的模块称为Python的标准库模块
语句 | 含义 |
---|---|
import 模块1[,模块2,...] | 完全导入 |
import ... as ... | 模块别名 |
具体操作:
import 语句
1 找到指定模块,初始化和加载它至内存中,若找不到,则抛出异常ImportError
2 import 所在的作用域的局部名称空间中,增加了名称和上一步创建的对象的关联(在某个函数内部写的impoer中的作用域中)
import 语句导入:
总结: 在当前模块中导入另一个模块,找到单独加载,单独初始化,生成模块对象,在自己的作用域内生成名称,将对象和名称进行映射,那个对象是单独生成的,和本模块(import所在的模块)没有多大关系,只是名称和其对象进行了映射
获取指定名称来收集对象的属性和方法
获取import 导入os.path的结果 ,此处只导入了os模块
as 重名称
总结 :
导入顶级模块,其名称对应的标识符加入到本地名称空间中,并绑定到初始化后的模块的位置
导入非顶级模块,其顶级模块对应的名称标识符会加入到本地名称空间中,导入的模块必须使用完全限定名成来访问
如果使用了as,其后面的名称会直接载入到本地名称空间中,并直接绑定到导入的模块对象
语句 | 含义 |
---|---|
from ... import | 部分导入 |
from ... import ... as ... | 别名 |
import 本质上只能导入模块。而from中可以对模块中的属性和方法内容进行导入操作
但其本质上还是将from中指定的模块全部都进行了初始化和加载操作
找到from子句中指定的模块,加载并初始化它(注意不是导入)
对于impoer字句后面的名称
1 先查看from字句导入的模块是否具有该名称属性
2 如果不是,则尝试导入该名称的子模块
3 还没有找到,则抛出ImportError异常
4 这个名称保存到本地名称空间中,如果有as字句,则使用as字句后的名称
1 模块名就是文件名
2 模块名必须符合标识符要求,非数字开头的数字,字母或下划线,不能是其他
3 不要使用系统模块,以避免冲突,除非你明确知道这个模块名的用途
4 通常模块名为全小写,下划线来分割
使用sys.path 来查看模块的搜索顺序
显示结果为python模块的路径搜索顺序
当加载一个模块的时候,需要从这些模块搜索路径中从前向后一次查找,不搜索这些目录的子目录,搜索到就进行加载,搜索不到就抛出异常路径可以是字典,zip文件,egg文件
.egg文件,由setuptools库创建的包,第三方常用的格式,添加了元数据(版本号,依赖项等)信息的zip文件
路径顺序为
程序主目录,程序运行的主程序脚本所在的目录
PYTHONPATH 目录,环境变量PYTHONPATH设置的目录也是搜索模块的路径
标准库目录,python自带的库摩克所在目录sys.path 是列表,可以被修改,Linux本身是走PATH,因此需要加上./x 而Windows本身路径就携带./
模块是不可以重复被导入的,重复导入是在浪费内存,其是在sys.modules中
从执行结果来看,不会产生重复导入的现象
所有加载的模块都会记录在sys.modules中。sys.modules存储已经加载过所有模块的字典
_name_ 每个模块都会定义一个_name_ 特殊变量来存储当前模块的名称,如果不指定,默认为源代码文件名词,如果有包则有限定名
解释器初始化的时候,会初始化字典sys.modules(保存已加载的模块),创建Builtins(全局函数,常量)模块、__main__模块,sys模块,以及模块搜索路径sys.path
python是脚本语言,任何一个脚本都可以直接执行,也可以作为模块被导入。
如果一个模块能够被执行,则就是main模块
当从标准输入(命令行方式敲代码),脚本或交互式读取的时候,会将模块的_name__设置为_main\,模块的顶层代码就在_main__这个作用域中执行,将_name__修改为__main\
顶层代码: 模块中缩进最外层的代码(当前解释器执行的环境)
如果是import 导入的,其_name_ 默认就是模块名创建一个自定义模块,并获取其模块名
目标模块中导入并打印相关模块名
1 本模块的功能测试
测试本模块内的函数,类2 避免主模块变更的副作用
顶层代码,没有封装,主模块使用没有问题,但是,一旦有了新的主模块,当前模块要被导入,由于源代码没有封装,则会一并被执行。
属性 | 含义 |
---|---|
_file_ | 字符串,源文件路径 |
_cached_ | 字符串,编译后的字节码文件路径 |
_spec_ | 显示模块的规范 |
_name_ | 模块名 |
_package_ | 当模块是包,同_name_,否则,可以设置为顶级模块的空字符串 |
普通文件天然是一个模块
创建一个普通文件夹,其是一个模块,无法在文件夹上写代码
添加一个模块n
此模块下面必须有一个.py的文件,其调用才有意义
此模块下创建.py文件为n1.py
导入并查看其类型
其自带_init_.py文件
导入结果对比如下
pycharm 中,创建Directory和创建python package 不同,前者是创建普通的目录,后者是创建一个带有_init_.py文件目录,及包
包目录下的py文件,子目录都是其子模块
三个模块嵌套,都是package,都写入print (_name_)用于获取包名称
在test中导入并查看如下
结论: 使用频率高文件中,使用频率多的应该放置在_init_.py中,因为模块在初始化过程中总会加载目录中的_init_.py文件及其中的内容,但其不会执行和导入其他相关子模块
若目录对应的_init_.py 不存在,则进行下一个对应的模块,作为一个好习惯是_init_.py文件必须有,python2中进行了限制,必须有,而python3中则限制不严,但建议必须存在
1 包能够更好的组织模块,尤其是大规模代码很多,可以拆分成很多子模块,便于使用某些功能就加载相应的子目录
包目录中_init_.py是包在第一次导入时就执行的,内容可以为空,也可以是用于该包的初始化工作的代码,最好不要删除它(低版本不可删除)
导入子模块一定会加载父模块,但导入父模块一定不会加载子模块
包之间只能使用.点号作为间隔符,表示模块及子目录的层级关系
模块也是封装,如同类,函数,不过他能够封装变量,类,函数
模块就是名称空间,其内部的顶层标识符,都是它的属性,可以通过_dict_ 或dir(module)查看
包也是模块,但模块不一定是包,包是特殊的模块,是一种组织方式,它包含__path__属性
凡是通过sys.path 找到的,都是绝对路径
绝对导入
在import语句或者from导入模块,模块名称最前面不是以.开头的
绝对导入总是去搜索模块搜索路径中找
相对导入
只能在包内使用,且只能用在from语句中
使用.点号,表示当前目录内
..表示上一级目录注意:不要在顶层模块中使用相对导入 (要参与运行的模块)
在w2层级进行导入其父层级
在顶层目录中导入子模块
进行在test模块中导入并查看
若在此顶层域中使用相对路径,则不行,因为其无法识别.和..等相关操作
定义__x和_y变量及z变量,并进行导入和访问处理
结论:此处未进行相关的保护操作和换名操作
结论:结果是只导入了公共属性,私有属性和保护变量属性都未曾导入
_all_ 是一个可迭代对象,元素是字符串,每一个元素都是一个模块内的变量名
导入模块如下
此处连之前的公共属性也没有了,只有对应写入的__all__的属性
若指定模块
普通变量,保护变量,私有变量,特殊变量,都没有被隐藏,也就是说模块内部没有私有变量,在模块中定义不做特殊处理。
其和dir()显示结果完全相同,但dir是列表,而locals是字典类型
局部作用域,locals和dir都有局部作用域的概念
写入子模块导入
导入查看
1 使用from ... import * 导入
A 如果模块中没有_all_。from ... import * 只能导入非下划线开头的模块的变量,如果是包,子模块也不会导入,除非在_all__中设置,或者在_init\.py中使用相对导入
B 如果模块中有_all_,from ... import * 只导入_all_ 列表中指定的名称,哪怕这个名词是下划线开头的,或者是子模块
C from ... import * 方式导入,使用简短,其副作用是会导入大量不需要使用的环境变量,甚至造成名称冲突,而_all_ 可以控制被导入模块在这种导入方式下能够提供的变量名称,就是为了阻止from ... import *导入过多的模块变量,从而避免冲突,因此,编写模块时,应该尽量加入_all_
2 from module import name1,name2 导入
这种方式的导入是明确的,哪怕是导入子模块,或者导入下划线开头的名称,程序员可以有控制和导入名称和其对应的对象
w1 的_init_.py中定义一个参数z
在test1 中引入并对其进行修改
在test中进入并进行查看
结论:
模块对象是同一个,因此模块的变量也是同一个,对模块变量的修改,会影响所有使用者,除非万不得已,或明确知道自己在做什么,否则不要修改模块的变量
前面已经学习过猴子补丁,也可以通过打补丁的方式,修改模块的变量,类,函数等内容
python 的模块或者源文件直接可以复制到项目中,便可以导入使用了,但为了更多项目的调用和使用,或者共享给别人,就需要进行打包,或者发布到网络上,便于其他人使用。目的是为了复用 。
本地使用的方式:
1 将模块或包放置到sys.path的搜索路径中即可
2 将此模块所在的路径加入到sys.path中即可,因为其是一个列表
1 distutils 官方库distutils,使用安装脚本setup.py来构建,安装包
2 setuptools
是替代distutils 的增强版本工具,包括easy_install工具,使用ez_setup.py 文件,支持egg格式的构建和安装
其能够提供查询,下载,安装,构建,发布,管理包等包管理功能setuptools 不再维护了。distribute是setuptools的替代品,其名字还是setuptools
3 pip
pip 是目前包管理的实施标准,构建在setuptools之上,代替easy_install的同时也提供了丰富的包管理功能,一般的,都会携带setuptools和easy_install
4 wheel
提供bdist_wheel作为setuptools的扩展命令,这个命令可以用来生成wheel打包格式,pip 提供了一个wheel子命令来安装wheel包,当然,需要先安装wheel模块,它可以让python库以二进制形式安装,而不需要在本地编译。
test 中包含自己的初始化文件_init_.py及模块test1.py 和包test2.py,test2.py中包含自己的初始化文件_init_.py和test21.py模块。
其路径在该包装的最外层。
内容如下
#!/usr/bin/poython3.6
#conding:utf-8
from distutils.core import setup
setup(
name='test', # 名字
version='0.1.0', #版本
description='Python test', #打包列表
author='zhang', # 作者
author_email='12345678910@163.com', #
# url 表示包帮助文档路径
packages=['test'] # 打包列表,指定test,会把w所有的非目录字母模块打包
)
目录结构
(zhangbing) [root@python python3.5]# python setup.py build
running build
running build_py
creating build
creating build/lib
creating build/lib/test
copying test/test1.py -> build/lib/test
copying test/__init__.py -> build/lib/test
(zhangbing) [root@python python3.5]# ls
build setup.py test
(zhangbing) [root@python python3.5]# tree build/
build/
└── lib
└── test
├── __init__.py
└── test1.py
2 directories, 2 files
(zhangbing) [root@python python3.5]#
此处只包含了init.py和test1,而没有穿透目录进入test2和test21
修改如下
#!/usr/bin/poython3.6
#conding:utf-8
from distutils.core import setup
setup(
name='test', # 名字
version='0.1.0', #版本
description='Python test', #打包列表
author='zhang', # 作者
author_email='12345678910@163.com', #
# url 表示包帮助文档路径
packages=['test.test2'] # 此时只会打印test2中的test21.py和__init__.py
)
删除原来打包结果 如下
(zhangbing) [root@python python3.5]# rm -rf build/
(zhangbing) [root@python python3.5]# ls
setup.py test
(zhangbing) [root@python python3.5]# python setup.py build
running build
running build_py
creating build
creating build/lib
creating build/lib/test
creating build/lib/test/test2
copying test/test2/test21.py -> build/lib/test/test2
copying test/test2/__init__.py -> build/lib/test/test2
(zhangbing) [root@python python3.5]# tree build/
build/
└── lib
└── test
└── test2
├── __init__.py
└── test21.py
3 directories, 2 files
要使得全部打包,则需要
#!/usr/bin/poython3.6
#conding:utf-8
from distutils.core import setup
setup(
name='test', # 名字
version='0.1.0', #版本
description='Python test', #打包列表
author='zhang', # 作者
author_email='12345678910@163.com', #
# url 表示包帮助文档路径
packages=['test','test.test2']
)
删除上述build,如下
(zhangbing) [root@python python3.5]# rm -rf build/
(zhangbing) [root@python python3.5]# python setup.py build
running build
running build_py
creating build
creating build/lib
creating build/lib/test
copying test/test1.py -> build/lib/test
copying test/__init__.py -> build/lib/test
creating build/lib/test/test2
copying test/test2/test21.py -> build/lib/test/test2
copying test/test2/__init__.py -> build/lib/test/test2
(zhangbing) [root@python python3.5]# tree build/
build/
└── lib
└── test
├── __init__.py
├── test1.py
└── test2
├── __init__.py
└── test21.py
3 directories, 4 files
build之后就可以install了,直接运行如下
(zhangbing) [root@python python3.5]# python setup.py install
running install
running build
running build_py
running install_lib
creating /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test
copying build/lib/test/test1.py -> /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test
copying build/lib/test/__init__.py -> /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test
creating /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2
copying build/lib/test/test2/test21.py -> /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2
copying build/lib/test/test2/__init__.py -> /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2
byte-compiling /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test1.py to test1.cpython-36.pyc
byte-compiling /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/__init__.py to __init__.cpython-36.pyc
byte-compiling /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2/test21.py to test21.cpython-36.pyc
byte-compiling /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2/__init__.py to __init__.cpython-36.pyc
running install_egg_info
Writing /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test-0.1.0-py3.6.egg-info
(zhangbing) [root@python python3.5]# ls
build setup.py test
(zhangbing) [root@python python3.5]# tree /root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-36.pyc
│ └── test1.cpython-36.pyc
├── test1.py
└── test2
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-36.pyc
│ └── test21.cpython-36.pyc
└── test21.py
3 directories, 8 files
其会自动添加到对应的第三方文件夹中,对应的是/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages
创建源代码的分发包
产生一个dist目录,里面生成一个带版本号的压缩包。
在其他地方解压这个文件,里面有setup.py,就可以使用python setup.py install 进行安装了,也可以使用pip install x.zip 直接安装这个压缩包
安装相关依赖包
yum -y install rpm-build
打包成rpm 如下
(zhangbing) [root@python python3.5]# rm -rf build/ dist/ MANIFEST
(zhangbing) [root@python python3.5]# ls
setup.py test
(zhangbing) [root@python python3.5]# python setup.py bdist_rpm
(zhangbing) [root@python python3.5]# python setup.py bdist_rpm
running bdist_rpm
creating build
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/rpm
creating build/bdist.linux-x86_64/rpm/SOURCES
creating build/bdist.linux-x86_64/rpm/SPECS
creating build/bdist.linux-x86_64/rpm/BUILD
creating build/bdist.linux-x86_64/rpm/RPMS
creating build/bdist.linux-x86_64/rpm/SRPMS
...
+ rm -rf test-0.1.0
+ exit 0
moving build/bdist.linux-x86_64/rpm/SRPMS/test-0.1.0-1.src.rpm -> dist
moving build/bdist.linux-x86_64/rpm/RPMS/noarch/test-0.1.0-1.noarch.rpm -> dist
结果如下
(zhangbing) [root@python python3.5]# ls
build dist MANIFEST setup.py test
(zhangbing) [root@python python3.5]# tree dist/
dist/
├── test-0.1.0-1.noarch.rpm
├── test-0.1.0-1.src.rpm
└── test-0.1.0.tar.gz
0 directories, 3 files
(zhangbing) [root@python python3.5]# tree build/
build/
└── bdist.linux-x86_64
└── rpm
├── BUILD
├── BUILDROOT
├── RPMS
│ └── noarch
├── SOURCES
│ └── test-0.1.0.tar.gz
├── SPECS
│ └── test.spec
└── SRPMS
9 directories, 2 files
此处在dict中生成了rpm包
(zhangbing) [root@python dist]# rpm -ivh test-0.1.0-1.noarch.rpm
准备中... ################################# [100%]
正在升级/安装...
1:test-0.1.0-1 ################################# [100%]
查看如下
(zhangbing) [root@python dist]# rpm -ql test
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test-0.1.0-py3.6.egg-info
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/__init__.py
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/__pycache__/__init__.cpython-36.opt-1.pyc
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/__pycache__/__init__.cpython-36.pyc
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/__pycache__/test1.cpython-36.opt-1.pyc
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/__pycache__/test1.cpython-36.pyc
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test1.py
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2/__init__.py
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2/__pycache__/__init__.cpython-36.opt-1.pyc
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2/__pycache__/__init__.cpython-36.pyc
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2/__pycache__/test21.cpython-36.opt-1.pyc
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2/__pycache__/test21.cpython-36.pyc
/root/.pyenv/versions/zhangbing/lib/python3.6/site-packages/test/test2/test21.py
动态导入: 运行时根据用户需求(提供字符串),找到模块的资源动态加载起来,相较于之前的导入,其import在编译期就决定的功能,是死的,不好用。
内建函数_import_()
相关参数
_import_(name,globals=None,locals=None,fromlist=(),level=0)
name,模块名,global全局生效,locals局部生效
sys=_import_('sys') 等价于 import sys
结构如下
代码如下
#!/usr/bin/poython3.6
#conding:utf-8
# this is test1
class A:
def show(self):
print ('this is test1')
#!/usr/bin/poython3.6
#conding:utf-8
# this is test
print (__import__('test1')) # 此处获取到一个模块对象
# 将模块赋值
mod=__import__('test1') # 给模块赋值
getattr(mod,'A')().show() # 此处调用模块中的A类并进行实例化后调用show方法
test结果如下
将此方式移动进入主模块中如下
#!/usr/bin/poython3.6
#conding:utf-8
# this is test
if __name__ == "__main__":
print(__import__('test1')) # 此处获取到一个模块对象
# 将模块赋值
mod = __import__('test1') # 给模块赋值
getattr(mod, 'A')().show() # 此处调用模块中的A类并进行实例化后调用show方法
结果也是相同,但别人在调用此模块时,其中的内容不会打印
进行函数化操作处理
#!/usr/bin/poython3.6
#conding:utf-8
# this is test
def plugin_load():
mod=__import__('test1')
getattr(mod,'A')().show()
if __name__ == "__main__":
# 在需要时进行动态加载
plugin_load()
结果如下:
上述方式只能每次加载一个,而不能实现多个加载,若想加载多个,则需要使用下面代码
多个加载
#!/usr/bin/poython3.6
#conding:utf-8
# this is test
def plugin_load(plugin_name:str,seq=":"):
module,_,clas =plugin_name.partition(seq) #通过此处切割将获取模块名和对应的内部的类或函数的属性名
mod=__import__(module)
cls=getattr(mod,clas)().show() # 获取属性并进行调用其方法,此处的返回有业务需求决定
return cls
if __name__ == "__main__":
# 在需要时进行动态加载
# 进行调用处理
plugin_load("test1:A")
结果如下
#!/usr/bin/poython3.6
#conding:utf-8
# this is test
def plugin_load(plugin_name:str,seq=":"):
module,_,clas =plugin_name.partition(seq) #通过此处切割将获取模块名和对应的内部的类或函数的属性名
mod=__import__(module)
cls=getattr(mod,clas) # 获取属性并进行调用其方法,此处的返回有业务需求决定
return cls() # 此处返回一个实例
if __name__ == "__main__":
# 在需要时进行动态加载
# 进行调用处理
plugin_load("test1:A").show()
结果如下
格式 importlib.import_module(name,package=None)
支持绝对导入和相对导入,如果是相对导入package必须设置
实例如下
#!/usr/bin/poython3.6
#conding:utf-8
# this is test
import importlib
def plugin_load(plugin_name:str,seq=":"):
module,_,clas =plugin_name.partition(seq) #通过此处切割将获取模块名和对应的内部的类或函数的属性名
mod=importlib.import_module(module)
cls=getattr(mod,clas) # 获取属性并进行调用其方法,此处的返回有业务需求决定
return cls() # 此处返回一个实例
if __name__ == "__main__":
# 在需要时进行动态加载
# 进行调用处理
plugin_load("test1:A").show()
结果如下
反射: 运行时获取类型的信息,可以动态维护类型数据
动态import: 推荐使用importlib模块,实现动态import模块的能力
多线程:可以开启一个线程,等待用户输入,从而加载指定名称的模块
加载的类型
1 程序启动时加载: 像pycharm这样的工具,需要很多组件,这些组件可能是插件,启动的时候扫描固定的目录加载插件
2 程序运行中: 程序运行过程中,接受用户指令或请求,启动相应的插件
优缺点:
两种方式各有利弊,如果插件很多,会导致程序启动很慢。如果用户需要时再加载,如果插件太大或者依赖太多,插件启动慢。所以必须先加载常用的插件,其他插件使用时,发现需要再插入
接口往往是暴露出来的功能,如模块提供的函数或方法,加载模块后调用这些函数完成功能,接口是一种规范,他约定了必须实现功能,但不关心如何实现此功能
插件是把模块加载到系统中,运行它,增强当前系统功能,或者提供系统不具备的功能,往往插件技术应用在框架设计中,系统本身设计简化、轻量级、实现基本功能后,其他功能通过插件加入进来,方便扩展。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。