python之模块和包
一 Python模块简介
1 模块化
一般来说,编程语言中,库,包,模块是同一种概念,是代码组织方式
python中只有一种模块对象类型,但是为了模块化组织的便利,提供了一个概念: 包
模块(module):指的是python的源代码文件
包(package):指的是模块组织在一起放入和包名同名的目录及相关文件创新互联公司专注为客户提供全方位的互联网综合服务,包含不限于网站建设、网站制作、嵩明网络推广、小程序定制开发、嵩明网络营销、嵩明企业策划、嵩明品牌公关、搜索引擎seo、人物专访、企业宣传片、企业代运营等,从售前售中售后,我们都将竭诚为您服务,您的肯定,是我们最大的嘉奖;创新互联公司为所有大学生创业者提供嵩明建站搭建服务,24小时服务热线:13518219792,官方网址:www.cdcxhl.com
可以将代码量较大的程序分割成多个有组织,彼此间独立但又能互相交互的代码片段,这些自我包含的有组织的代码段就是模块
模块在物理形式上表现为以.py 结尾的代码文件
一个文件被看做一个独立的模块,一个模块也可以被看做是一个文件
模块的文件名就是模块的名字加上扩展名.py
2 模块名称空间
每个模块都有自己的名称空间
Python 允许“导入”其他模块以实现代码重用,从而也实现了将独立的代码文件组织成更大的程序系统
Python 中,模块也是对象
在一个模块的顶层定义(全局变量)的所有变量都在被导入时成为了被导入模块的属性
3 顶层文件和模块文件
一个Python程序通常包括一个顶层文件和其他的模块文件(0个,1个或多个)
顶层文件:包含了程序的主要控制流程
模块文件:为顶层文件或其他模块提供各种功能性组件
模块首次导入(或重载)时,Python会立即执行模块文件的顶层程序代码(不在函数内的代码),而位于函数体内的代码直到函数被调用后才会执行
Python自带的模块称为Python的标准库模块
二 import 和 from .. import 及自定义模块
1 import 导入语句
语句 | 含义 |
---|---|
import 模块1[,模块2,...] | 完全导入 |
import ... as ... | 模块别名 |
具体操作:
import 语句
1 找到指定模块,初始化和加载它至内存中,若找不到,则抛出异常ImportError
2 import 所在的作用域的局部名称空间中,增加了名称和上一步创建的对象的关联(在某个函数内部写的impoer中的作用域中)
import 语句导入:
总结: 在当前模块中导入另一个模块,找到单独加载,单独初始化,生成模块对象,在自己的作用域内生成名称,将对象和名称进行映射,那个对象是单独生成的,和本模块(import所在的模块)没有多大关系,只是名称和其对象进行了映射
获取指定名称来收集对象的属性和方法
获取import 导入os.path的结果 ,此处只导入了os模块
as 重名称
总结 :
导入顶级模块,其名称对应的标识符加入到本地名称空间中,并绑定到初始化后的模块的位置
导入非顶级模块,其顶级模块对应的名称标识符会加入到本地名称空间中,导入的模块必须使用完全限定名成来访问
如果使用了as,其后面的名称会直接载入到本地名称空间中,并直接绑定到导入的模块对象
2 部分导入 (from ... import ...)
1 语法
语句 | 含义 |
---|---|
from ... import | 部分导入 |
from ... import ... as ... | 别名 |
2 导入
import 本质上只能导入模块。而from中可以对模块中的属性和方法内容进行导入操作
但其本质上还是将from中指定的模块全部都进行了初始化和加载操作
3 as 字句的使用
4 总结
找到from子句中指定的模块,加载并初始化它(注意不是导入)
对于impoer字句后面的名称
1 先查看from字句导入的模块是否具有该名称属性
2 如果不是,则尝试导入该名称的子模块
3 还没有找到,则抛出ImportError异常
4 这个名称保存到本地名称空间中,如果有as字句,则使用as字句后的名称
3 自定义模块
1 自定义模块test
2 import 方式导入模块,其名词边界是模块,不是方法或属性。
3 from ... import ... 方式导入模块
4 自定义模块命名规范:
1 模块名就是文件名
2 模块名必须符合标识符要求,非数字开头的数字,字母或下划线,不能是其他
3 不要使用系统模块,以避免冲突,除非你明确知道这个模块名的用途
4 通常模块名为全小写,下划线来分割
三 模块相关属性
1 模块的搜索顺序
使用sys.path 来查看模块的搜索顺序
显示结果为python模块的路径搜索顺序
当加载一个模块的时候,需要从这些模块搜索路径中从前向后一次查找,不搜索这些目录的子目录,搜索到就进行加载,搜索不到就抛出异常路径可以是字典,zip文件,egg文件
.egg文件,由setuptools库创建的包,第三方常用的格式,添加了元数据(版本号,依赖项等)信息的zip文件
路径顺序为
程序主目录,程序运行的主程序脚本所在的目录
PYTHONPATH 目录,环境变量PYTHONPATH设置的目录也是搜索模块的路径
标准库目录,python自带的库摩克所在目录sys.path 是列表,可以被修改,Linux本身是走PATH,因此需要加上./x 而Windows本身路径就携带./
2 模块的重复导入
模块是不可以重复被导入的,重复导入是在浪费内存,其是在sys.modules中
从执行结果来看,不会产生重复导入的现象
所有加载的模块都会记录在sys.modules中。sys.modules存储已经加载过所有模块的字典
3 模块的运行
_name_ 每个模块都会定义一个_name_ 特殊变量来存储当前模块的名称,如果不指定,默认为源代码文件名词,如果有包则有限定名
解释器初始化的时候,会初始化字典sys.modules(保存已加载的模块),创建Builtins(全局函数,常量)模块、__main__模块,sys模块,以及模块搜索路径sys.path
python是脚本语言,任何一个脚本都可以直接执行,也可以作为模块被导入。
如果一个模块能够被执行,则就是main模块
当从标准输入(命令行方式敲代码),脚本或交互式读取的时候,会将模块的_name__设置为_main\,模块的顶层代码就在_main__这个作用域中执行,将_name__修改为__main\
顶层代码: 模块中缩进最外层的代码(当前解释器执行的环境)
如果是import 导入的,其_name_ 默认就是模块名创建一个自定义模块,并获取其模块名
目标模块中导入并打印相关模块名
4 if name== 'main': 用途
1 本模块的功能测试
测试本模块内的函数,类2 避免主模块变更的副作用
顶层代码,没有封装,主模块使用没有问题,但是,一旦有了新的主模块,当前模块要被导入,由于源代码没有封装,则会一并被执行。
5模块内部其他的属性
属性 | 含义 |
---|---|
_file_ | 字符串,源文件路径 |
_cached_ | 字符串,编译后的字节码文件路径 |
_spec_ | 显示模块的规范 |
_name_ | 模块名 |
_package_ | 当模块是包,同_name_,否则,可以设置为顶级模块的空字符串 |
四 模块和包
1 模块
普通文件天然是一个模块
创建一个普通文件夹,其是一个模块,无法在文件夹上写代码
添加一个模块n
此模块下面必须有一个.py的文件,其调用才有意义
此模块下创建.py文件为n1.py
导入并查看其类型
2 创建包
其自带_init_.py文件
导入结果对比如下
pycharm 中,创建Directory和创建python package 不同,前者是创建普通的目录,后者是创建一个带有_init_.py文件目录,及包
3 子模块
包目录下的py文件,子目录都是其子模块
三个模块嵌套,都是package,都写入print (_name_)用于获取包名称
在test中导入并查看如下
结论: 使用频率高文件中,使用频率多的应该放置在_init_.py中,因为模块在初始化过程中总会加载目录中的_init_.py文件及其中的内容,但其不会执行和导入其他相关子模块
若目录对应的_init_.py 不存在,则进行下一个对应的模块,作为一个好习惯是_init_.py文件必须有,python2中进行了限制,必须有,而python3中则限制不严,但建议必须存在
4 模块和包的总结:
1 包能够更好的组织模块,尤其是大规模代码很多,可以拆分成很多子模块,便于使用某些功能就加载相应的子目录
包目录中_init_.py是包在第一次导入时就执行的,内容可以为空,也可以是用于该包的初始化工作的代码,最好不要删除它(低版本不可删除)
导入子模块一定会加载父模块,但导入父模块一定不会加载子模块
包之间只能使用.点号作为间隔符,表示模块及子目录的层级关系
模块也是封装,如同类,函数,不过他能够封装变量,类,函数
模块就是名称空间,其内部的顶层标识符,都是它的属性,可以通过_dict_ 或dir(module)查看
包也是模块,但模块不一定是包,包是特殊的模块,是一种组织方式,它包含__path__属性
5 绝对导入和相对导入
1 概念
凡是通过sys.path 找到的,都是绝对路径
绝对导入
在import语句或者from导入模块,模块名称最前面不是以.开头的
绝对导入总是去搜索模块搜索路径中找
相对导入
只能在包内使用,且只能用在from语句中
使用.点号,表示当前目录内
..表示上一级目录注意:不要在顶层模块中使用相对导入 (要参与运行的模块)
2 导入实战
在w2层级进行导入其父层级
在顶层目录中导入子模块
进行在test模块中导入并查看
若在此顶层域中使用相对路径,则不行,因为其无法识别.和..等相关操作
6 访问控制
1 定义变量
定义__x和_y变量及z变量,并进行导入和访问处理
2 导入并访问查看
结论:此处未进行相关的保护操作和换名操作
3 使用from w import * 导入
结论:结果是只导入了公共属性,私有属性和保护变量属性都未曾导入
4 引入__all__模块
_all_ 是一个可迭代对象,元素是字符串,每一个元素都是一个模块内的变量名
导入模块如下
此处连之前的公共属性也没有了,只有对应写入的__all__的属性
若指定模块
普通变量,保护变量,私有变量,特殊变量,都没有被隐藏,也就是说模块内部没有私有变量,在模块中定义不做特殊处理。
5 locals
其和dir()显示结果完全相同,但dir是列表,而locals是字典类型
局部作用域,locals和dir都有局部作用域的概念
写入子模块导入
导入查看
6 总结:
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 导入
这种方式的导入是明确的,哪怕是导入子模块,或者导入下划线开头的名称,程序员可以有控制和导入名称和其对应的对象
7 模块变量的修改
w1 的_init_.py中定义一个参数z
在test1 中引入并对其进行修改
在test中进入并进行查看
结论:
模块对象是同一个,因此模块的变量也是同一个,对模块变量的修改,会影响所有使用者,除非万不得已,或明确知道自己在做什么,否则不要修改模块的变量
前面已经学习过猴子补丁,也可以通过打补丁的方式,修改模块的变量,类,函数等内容
五 包管理
1 为什么要使用包管理
python 的模块或者源文件直接可以复制到项目中,便可以导入使用了,但为了更多项目的调用和使用,或者共享给别人,就需要进行打包,或者发布到网络上,便于其他人使用。目的是为了复用 。
本地使用的方式:
1 将模块或包放置到sys.path的搜索路径中即可
2 将此模块所在的路径加入到sys.path中即可,因为其是一个列表
2 主要工具
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库以二进制形式安装,而不需要在本地编译。
3 使用setup.py 打包
1 包结构如下 :
test 中包含自己的初始化文件_init_.py及模块test1.py 和包test2.py,test2.py中包含自己的初始化文件_init_.py和test21.py模块。
2 创建setup.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所有的非目录字母模块打包
)
3 打包
目录结构
(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
4 install 安装命令
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
4 命令分发
1 sdist命令简介
创建源代码的分发包
产生一个dist目录,里面生成一个带版本号的压缩包。
在其他地方解压这个文件,里面有setup.py,就可以使用python setup.py install 进行安装了,也可以使用pip install x.zip 直接安装这个压缩包
2 打包操作
安装相关依赖包
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包
3 安装如下
(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
六 插件化开发基本概述
1 概述
动态导入: 运行时根据用户需求(提供字符串),找到模块的资源动态加载起来,相较于之前的导入,其import在编译期就决定的功能,是死的,不好用。
内建函数_import_()
相关参数
_import_(name,globals=None,locals=None,fromlist=(),level=0)
name,模块名,global全局生效,locals局部生效
sys=_import_('sys') 等价于 import sys
2 实例如下
结构如下
代码如下
#!/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()
结果如下
3 import_module
格式 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()
结果如下
4 插件化编程技术概述
1 依赖技术
反射: 运行时获取类型的信息,可以动态维护类型数据
动态import: 推荐使用importlib模块,实现动态import模块的能力
多线程:可以开启一个线程,等待用户输入,从而加载指定名称的模块
2 加载时机
加载的类型
1 程序启动时加载: 像pycharm这样的工具,需要很多组件,这些组件可能是插件,启动的时候扫描固定的目录加载插件
2 程序运行中: 程序运行过程中,接受用户指令或请求,启动相应的插件
优缺点:
两种方式各有利弊,如果插件很多,会导致程序启动很慢。如果用户需要时再加载,如果插件太大或者依赖太多,插件启动慢。所以必须先加载常用的插件,其他插件使用时,发现需要再插入
3 接口和插件区别
接口往往是暴露出来的功能,如模块提供的函数或方法,加载模块后调用这些函数完成功能,接口是一种规范,他约定了必须实现功能,但不关心如何实现此功能
插件是把模块加载到系统中,运行它,增强当前系统功能,或者提供系统不具备的功能,往往插件技术应用在框架设计中,系统本身设计简化、轻量级、实现基本功能后,其他功能通过插件加入进来,方便扩展。
网页题目:python之模块和包
文章URL:http://hbruida.cn/article/pieejj.html