python之模块和包

2023-01-31 02:01:14 python 模块

1 模块化

一般来说,编程语言中,库,包,模块是同一种概念,是代码组织方式
python中只有一种模块对象类型,但是为了模块化组织的便利,提供了一个概念: 包
模块(module):指的是Python的源代码文件
包(package):指的是模块组织在一起放入和包名同名的目录及相关文件


可以将代码量较大的程序分割成多个有组织,彼此间独立但又能互相交互的代码片段,这些自我包含的有组织的代码段就是模块


模块在物理形式上表现为以.py 结尾的代码文件
一个文件被看做一个独立的模块,一个模块也可以被看做是一个文件
模块的文件名就是模块的名字加上扩展名.py


2 模块名称空间

每个模块都有自己的名称空间
Python 允许“导入”其他模块以实现代码重用,从而也实现了将独立的代码文件组织成更大的程序系统


Python 中,模块也是对象
在一个模块的顶层定义(全局变量)的所有变量都在被导入时成为了被导入模块的属性

3 顶层文件和模块文件

一个Python程序通常包括一个顶层文件和其他的模块文件(0个,1个或多个)
顶层文件:包含了程序的主要控制流程
模块文件:为顶层文件或其他模块提供各种功能性组件
模块首次导入(或重载)时,Python会立即执行模块文件的顶层程序代码(不在函数内的代码),而位于函数体内的代码直到函数被调用后才会执行
Python自带的模块称为Python的标准库模块

1 import 导入语句

语句 含义
import 模块1[,模块2,...] 完全导入
import ... as ... 模块别名

具体操作:

import 语句
1 找到指定模块,初始化和加载它至内存中,若找不到,则抛出异常ImportError
2 import 所在的作用域的局部名称空间中,增加了名称和上一步创建的对象的关联(在某个函数内部写的impoer中的作用域中)

import 语句导入:
python之模块和包

总结: 在当前模块中导入另一个模块,找到单独加载,单独初始化,生成模块对象,在自己的作用域内生成名称,将对象和名称进行映射,那个对象是单独生成的,和本模块(import所在的模块)没有多大关系,只是名称和其对象进行了映射


获取指定名称来收集对象的属性和方法

python之模块和包

获取import 导入os.path的结果 ,此处只导入了os模块

python之模块和包

as 重名称

python之模块和包

总结 :
导入顶级模块,其名称对应的标识符加入到本地名称空间中,并绑定到初始化后的模块的位置
导入非顶级模块,其顶级模块对应的名称标识符会加入到本地名称空间中,导入的模块必须使用完全限定名成来访问
如果使用了as,其后面的名称会直接载入到本地名称空间中,并直接绑定到导入的模块对象

2 部分导入 (from ... import ...)

1 语法

语句 含义
from ... import 部分导入
from ... import ... as ... 别名

2 导入

python之模块和包

import 本质上只能导入模块。而from中可以对模块中的属性和方法内容进行导入操作
但其本质上还是将from中指定的模块全部都进行了初始化和加载操作

python之模块和包

python之模块和包

3 as 字句的使用

python之模块和包

4 总结

找到from子句中指定的模块,加载并初始化它(注意不是导入)
对于impoer字句后面的名称
1 先查看from字句导入的模块是否具有该名称属性
2 如果不是,则尝试导入该名称的子模块
3 还没有找到,则抛出ImportError异常
4 这个名称保存到本地名称空间中,如果有as字句,则使用as字句后的名称

3 自定义模块

1 自定义模块test

python之模块和包

2 import 方式导入模块,其名词边界是模块,不是方法或属性。

python之模块和包

3 from ... import ... 方式导入模块

python之模块和包

4 自定义模块命名规范:

1 模块名就是文件名
2 模块名必须符合标识符要求,非数字开头的数字,字母或下划线,不能是其他
3 不要使用系统模块,以避免冲突,除非你明确知道这个模块名的用途
4 通常模块名为全小写,下划线来分割

1 模块的搜索顺序

使用sys.path 来查看模块的搜索顺序

python之模块和包

显示结果为python模块的路径搜索顺序
当加载一个模块的时候,需要从这些模块搜索路径中从前向后一次查找,不搜索这些目录的子目录,搜索到就进行加载,搜索不到就抛出异常

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

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


路径顺序为
程序主目录,程序运行的主程序脚本所在的目录
PYTHONPATH 目录,环境变量PYTHONPATH设置的目录也是搜索模块的路径
标准库目录,python自带的库摩克所在目录

sys.path 是列表,可以被修改,linux本身是走PATH,因此需要加上./x 而windows本身路径就携带./

2 模块的重复导入

模块是不可以重复被导入的,重复导入是在浪费内存,其是在sys.modules中

python之模块和包

从执行结果来看,不会产生重复导入的现象
所有加载的模块都会记录在sys.modules中。sys.modules存储已经加载过所有模块的字典

3 模块的运行

_name_ 每个模块都会定义一个_name_ 特殊变量来存储当前模块的名称,如果不指定,默认为源代码文件名词,如果有包则有限定名
解释器初始化的时候,会初始化字典sys.modules(保存已加载的模块),创建Builtins(全局函数,常量)模块、__main__模块,sys模块,以及模块搜索路径sys.path


python是脚本语言,任何一个脚本都可以直接执行,也可以作为模块被导入。


如果一个模块能够被执行,则就是main模块

当从标准输入(命令行方式敲代码),脚本或交互式读取的时候,会将模块的_name__设置为_main\,模块的顶层代码就在_main__这个作用域中执行,将_name__修改为__main\


顶层代码: 模块中缩进最外层的代码(当前解释器执行的环境)
如果是import 导入的,其_name_ 默认就是模块名

创建一个自定义模块,并获取其模块名

python之模块和包

目标模块中导入并打印相关模块名

python之模块和包

4 if name== 'main': 用途

1 本模块的功能测试
测试本模块内的函数,类

2 避免主模块变更的副作用
顶层代码,没有封装,主模块使用没有问题,但是,一旦有了新的主模块,当前模块要被导入,由于源代码没有封装,则会一并被执行。

5模块内部其他的属性

属性 含义
_file_ 字符串,源文件路径
_cached_ 字符串,编译后的字节码文件路径
_spec_ 显示模块的规范
_name_ 模块名
_package_ 当模块是包,同_name_,否则,可以设置为顶级模块的空字符串

python之模块和包

1 模块

普通文件天然是一个模块
创建一个普通文件夹,其是一个模块,无法在文件夹上写代码
添加一个模块n

python之模块和包

此模块下面必须有一个.py的文件,其调用才有意义
此模块下创建.py文件为n1.py

python之模块和包

导入并查看其类型

python之模块和包

2 创建包

python之模块和包

其自带_init_.py文件

python之模块和包

导入结果对比如下

python之模块和包

PyCharm 中,创建Directory和创建python package 不同,前者是创建普通的目录,后者是创建一个带有_init_.py文件目录,及包

3 子模块

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

python之模块和包

三个模块嵌套,都是package,都写入print (_name_)用于获取包名称

在test中导入并查看如下

python之模块和包

结论: 使用频率高文件中,使用频率多的应该放置在_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层级进行导入其父层级

python之模块和包

在顶层目录中导入子模块

python之模块和包

进行在test模块中导入并查看

python之模块和包

若在此顶层域中使用相对路径,则不行,因为其无法识别.和..等相关操作

6 访问控制

1 定义变量

定义__x和_y变量及z变量,并进行导入和访问处理

python之模块和包

2 导入并访问查看

python之模块和包

结论:此处未进行相关的保护操作和换名操作

3 使用from w import * 导入

python之模块和包

结论:结果是只导入了公共属性,私有属性和保护变量属性都未曾导入

4 引入__all__模块

_all_ 是一个可迭代对象,元素是字符串,每一个元素都是一个模块内的变量名

python之模块和包

导入模块如下

python之模块和包

此处连之前的公共属性也没有了,只有对应写入的__all__的属性

若指定模块

python之模块和包

普通变量,保护变量,私有变量,特殊变量,都没有被隐藏,也就是说模块内部没有私有变量,在模块中定义不做特殊处理。

5 locals

python之模块和包

其和dir()显示结果完全相同,但dir是列表,而locals是字典类型

局部作用域,locals和dir都有局部作用域的概念

python之模块和包

写入子模块导入

python之模块和包

导入查看

python之模块和包

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

python之模块和包

在test1 中引入并对其进行修改

python之模块和包

在test中进入并进行查看

python之模块和包

结论:
模块对象是同一个,因此模块的变量也是同一个,对模块变量的修改,会影响所有使用者,除非万不得已,或明确知道自己在做什么,否则不要修改模块的变量


前面已经学习过猴子补丁,也可以通过打补丁的方式,修改模块的变量,类,函数等内容

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 包结构如下 :

python之模块和包

test 中包含自己的初始化文件_init_.py及模块test1.py 和包test2.py,test2.py中包含自己的初始化文件_init_.py和test21.py模块。

2 创建setup.py文件

python之模块和包

其路径在该包装的最外层。

内容如下

#!/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 打包

目录结构

python之模块和包
python之模块和包

(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 实例如下

结构如下

python之模块和包

代码如下

#!/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结果如下

python之模块和包

将此方式移动进入主模块中如下

#!/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()

结果如下:
python之模块和包

上述方式只能每次加载一个,而不能实现多个加载,若想加载多个,则需要使用下面代码

多个加载

#!/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")

结果如下

python之模块和包

#!/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()

结果如下

python之模块和包

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()

结果如下

python之模块和包

4 插件编程技术概述

1 依赖技术

反射: 运行时获取类型的信息,可以动态维护类型数据
动态import: 推荐使用importlib模块,实现动态import模块的能力
多线程:可以开启一个线程,等待用户输入,从而加载指定名称的模块

2 加载时机

加载的类型
1 程序启动时加载: 像pycharm这样的工具,需要很多组件,这些组件可能是插件,启动的时候扫描固定的目录加载插件
2 程序运行中: 程序运行过程中,接受用户指令或请求,启动相应的插件


优缺点:
两种方式各有利弊,如果插件很多,会导致程序启动很慢。如果用户需要时再加载,如果插件太大或者依赖太多,插件启动慢。所以必须先加载常用的插件,其他插件使用时,发现需要再插入

3 接口和插件区别

接口往往是暴露出来的功能,如模块提供的函数或方法,加载模块后调用这些函数完成功能,接口是一种规范,他约定了必须实现功能,但不关心如何实现此功能


插件是把模块加载到系统中,运行它,增强当前系统功能,或者提供系统不具备的功能,往往插件技术应用在框架设计中,系统本身设计简化、轻量级、实现基本功能后,其他功能通过插件加入进来,方便扩展。

相关文章