python之模块和包

发布时间:2019-08-25 09:36:43编辑:auto阅读(1802)

    一 Python模块简介

    1 模块化

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


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


    模块在物理形式上表现为以.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 语句导入:
    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 接口和插件区别

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


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

关键字