• 模块与包——


    模块与包

    模块是个啥

    模块是一系列功能的集合体,分为三大类:

    1. 内置模块
    2. 第三方模块
    3. 自定义的模块

    一个Python文件本身就是一个模块,文件名m.py,模块名叫m

    __init__.py文件,称为包

    为何有模块

    1. 内置模块与第三方拿来就用,无需定义,可以提升开发效率
    2. 自定义模块,可以将程序的各部分功能提取出来放到一个模块中,供大家共享,好处是减少代码冗余,程序组织结构清晰

    1.1、首次导入模块发生的三件事

    • 1.执行xx.py
    • 2.产生xx.py的名称空间,将xx.py运行过程中产生的名字都丢带xx的名称空间中
    • 3.在当前文件中产生的有一个名字xx,该名字指向2中产生的名称空间

    首次导入完后,之后再导入都是直接引用首次导入产生的xx.py的名称空间,不会重复执行代码

    1.2、import导入模块使用

    """
    
    1. 可以使用逗号分隔符在一行导入多个模块
    import time,sys
    2. 导入模块的规范(导入的顺序)
    	1.python内置的模块
    	2.第三方模块
    	3.自定义模块
    3. 将导入的模块起个别名(当模块名比较长的时候用as起别名)
    import time  as t
    4. 自定义模块命名应该采用纯小写+下划线风格
    5. 可以在函数内导入模块,对比在文件开头导入模块属于全局作用域,在函数内导入的模块则属于局部的作用域。
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    一个python文件的两种用途

    • 被当作模块导入
    • 被当作程序运行

    一个Python文件有两种用途,一种被当主程序/脚本执行,另一种被当模块导入,为了区别同一个文件的不同用途,每个py文件都内置了__name__变量,该变量在py文件被当做脚本执行时赋值为“main”,在py文件被当做模块导入时赋值为模块名

    print(__name__)
    #1. 当xx.py被运行时,__name__的值为__main__
    #2. 当xx.py被当作模块导入时,__name__的值为'xx'
    
    if __name__ == '__main__':
    	print("文件被执行")
    else:
        #被当做模块导入的时做到事情
        print("文件被导入")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.3、from …import 导入

    """
    import导入模块在使用时必须加前缀"模块."
    	优点:肯定不会与当前名称空间中的名字冲突
    	缺点:加前缀显得麻烦
    
    from ...import ...导入也发生了三件事
    	1.产生一个模块的名称空间
    	2.运行xx.py将运行过程中产生的名字都丢到模块的名称空间去
    	3. 在当前名称空间拿到一个名字,该名字与模块名称空间中的某一个内存地址
    	优点:代码更精简
    	缺点:容易与当前名称空间混淆
    	
    	一行可以导入多个名字
    	from time import time,sleep
    	*:导入模块中的所有名字
    	from os import *
    	
    """
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果我们需要引用模块中的名字过多的话,可以采用上述的导入形式来达到节省代码量的效果,但是需要强调的一点是:只能在模块最顶层使用的方式导入,在函数内则非法,并且的方式会带来一种副作用,即我们无法搞清楚究竟从源文件中导入了哪些名字到当前位置,这极有可能与当前位置的名字产生冲突。模块的编写者可以在自己的文件中定义__all__变量用来控制*代表的意思

    #foo.py
    __all__=['x','get'] #该列表中所有的元素必须是字符串类型,每个元素对应foo.py中的一个名字
    x=1
    def get():
        print(x)
    def change():
        global x
        x=0
    class Foo:
        def func(self):
           print('from the func')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样我们在另外一个文件中使用*导入时,就只能导入__all__定义的名字了

    from foo import * #此时的*只代表x和get
    
    x #可用
    get() #可用
    change() #不可用
    Foo() #不可用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.4、循环导入问题

    1.5、模块搜索优先级

    模块其实分为四个通用类别,分别是:

    1、使用纯Python代码编写的py文件

    2、包含一系列模块的包

    3、使用C编写并链接到Python解释器中的内置模块

    4、使用C或C++编译的扩展模块

    优先级

    在导入一个模块时,如果该模块已加载到内存中,则直接引用,否则会优先查找内置模块,然后按照从左到右的顺序依次检索sys.path中定义的路径,直到找模块对应的文件为止,否则抛出异常。

    sys.path也被称为模块的搜索路径,它是一个列表类型

    import sys
    print(sys.path)#值为一个列表,存放了一系列的文件夹,其中第一个文件夹是当前执行文件所在的文件夹
    
    """结果如下"""
    ['E:\\Python\\python\\demo\\day04', 'E:\\Python\\python\\demo', 'D:\\Catalog\\JetBrains\\PyCharm 2022.2\\plugins\\python\\helpers\\pycharm_display', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310\\python310.zip', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310\\DLLs', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310\\lib', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310', 'C:\\Users\\31812\\AppData\\Local\\Programs\\Python\\Python310\\lib\\site-packages', 'D:\\Catalog\\JetBrains\\PyCharm 2022.2\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    列表中的每个元素其实都可以当作一个目录来看:在列表中会发现有.zip或.egg结尾的文件,二者是不同形式的压缩文件,事实上Python确实支持从一个压缩文件中导入模块,我们也只需要把它们都当成目录去看即可。

    sys.path中的第一个路径通常为空,代表执行文件所在的路径,所以在被导入模块与执行文件在同一目录下时肯定是可以正常导入的,而针对被导入的模块与执行文件在不同路径下的情况,为了确保模块对应的源文件仍可以被找到,需要将源文件foo.py所在的路径添加到sys.path中,假设foo.py所在的路为/pythoner/projects/

    import sys
    #找foo.py,就把foo.py的文件夹添加到环境变量中
    sys.path.append(r'/pythoner/projects/') 
    
    import foo #无论foo.py在何处,我们都可以导入它了
    
    • 1
    • 2
    • 3
    • 4
    • 5

    sys.modules查看已经加载带内存中的模块

    print('demo1' in sys.modules)
    
    • 1

    1.6、代码规范

    我们在编写py文件时,需要时刻提醒自己,该文件既是给自己用的,也有可能会被其他人使用,因而代码的可读性与易维护性显得十分重要,为此我们在编写一个模块时最好按照统一的规范去编写,如下

    #!/usr/bin/env python #通常只在类unix环境有效,作用是可以使用脚本名来执行,而无需直接调用解释器。
    
    "The module is used to..." #模块的文档描述
    
    import sys #导入模块
    
    x=1 #定义全局变量,如果非必须,则最好使用局部变量,这样可以提高代码的易维护性,并且可以节省内存提高性能
    
    class Foo: #定义类,并写好类的注释
        'Class Foo is used to...'
        pass
    
    def test(): #定义函数,并写好函数的注释
        'Function test is used to…'
        pass
    
    if __name__ == '__main__': #主程序
        test() #在被当做脚本执行时,执行此处的代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    1.7、函数的提示类型

    def register(name:str,age:int,hobby:tuple)->int:# ->int表示返回值为整型,冒号跟的是提示信息
        print(name)
        print(age)
        print(hobby)
        return 111
    # register(1,'aaa',[1,])
    register('lisi',22,('JayChou',))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1.8、包

    1. 包就是一个包含__init__.py文件的文件夹
    2. 包的本质就是模块的一种形式,是用来被当作模块导入

    导入包发生的三件事

    • 1.产生一个名称空间
    • 2.运行包下的__init__.py文件,将运行过程中产生的名字都丢带1的名称空间中
    • 3.在当前执行文件的名称空间中拿到一个名字xx,xx指向1的名称空间
    在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
    
    #2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包的本质就是一种模
    
    • 1
    • 2
    • 3

    强调

    1.关于包相关的导入语句也分为importfrom ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如import 顶级包.子包.子模块,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)2、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间
    
    3import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
    
    • 1
    • 2
    • 3
    • 4
    • 5

    针对包内的模块之间互相导入,导入的方式有两种

    1、绝对导入:以顶级包为起始

    #demo下的__init__.py
    from demo import versions
    from demo.a.m1 import b
    
    • 1
    • 2
    • 3
    import也能使用绝对导入,导入过程中同样会依次执行包下的__init__.py,只是基于import导入的结果,使用时必须加上该前缀
    
    • 1

    2、相对导入:.代表当前文件所在的目录,…代表当前目录的上一级目录,依此类推

    #demo下的__init__.py
    from . import versions
    
    • 1
    • 2

    总结

    包的使用需要牢记三点
    1、导包就是在导包下__init__.py文件
    2、包内部的导入应该使用相对导入,相对导入也只能在包内部使用,而且…取上一级不能出包
    3、
    使用语句中的点代表的是访问属性
    m.n.x ----> 向m要n,向n要x
    而导入语句中的点代表的是路径分隔符
    import a.b.c --> a/b/c,文件夹下a下有子文件夹b,文件夹b下有子文件或文件夹c
    所以导入语句中点的左边必须是一个包

    **from 包 import ***

    在使用包时同样支持from pool.futures import * ,毫无疑问*代表的是futures下__init__.py中所有的名字,通用是用变量__all__来控制*代表的意思

    #futures下的__init__.py
    __all__=['process','thread']
    
    • 1
    • 2

  • 相关阅读:
    【zookeeper】报错整理 zookeeper Packet len* is out of range
    微服务-gateway跨域配置
    Tomcat任意文件上传漏洞(CVE-2017-12615)
    计算机网络(一)
    ArcGIS Pro怎么生成高程点
    【信号处理】基于均分原理的同步信号分割与建模附matlab代码
    如何规避需求分析缺陷,提高需求分析质量?
    基于 nodejs+vue城市轨道交通线路查询系统mysql
    vue3中使用全局自定义指令和组件自定义指令
    ShardingSphere-proxy-5.0.0建立mysql读写分离的连接(六)
  • 原文地址:https://blog.csdn.net/ZhaoSong_/article/details/126842234