• 模块


    1.模块介绍

    1.1 模块的概念

    模块是什么 ?

    模块就是一系列功能(函数)的集合体

    模块的分类

    • 内置模块 c语言写的模块
    • 第三方模块 别人写好的模块
    • 自定义模块 自己自定义的

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

    ps:模块分为4中形式
        1.使用python编写的.py文件
        2.已被编译为共享库或DLL的C或C++扩展
        3.把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包
        4.使用C编写并链接到python解释器的内置模块
    

    1.2 为什么要用模块

    1. 内置与第三方的模块拿来就用,无需定义,这种拿来主义,可以极大的提升自己的开发效率
    2. 可以将程序的各部分功能提取出来放到一模块中为大家共享使用
    3. 减少了代码冗余,程序组织结构更加清晰

    2. 模块的两种导入方式

    2.1 import

    [创建模块]

    # m.py
    print('======m=====')
    x = 100
    def get():
        print("get ========%s" % x)
    
    
    def change():
        global x
        x += 1
        return x
    

    [导入模块]

    # demo.py
    import m     # 导入m模块
    
    x = 1
    y = 2
    
    # ======m=====  
    # 打印l ======m===== ,说明导入模块,会运行模块里面的代码
    

    [思考] 首次模块会发生什么事情

    1.产生m.py的名称空间
    2.执行m.py代码,将m.py运行过程中产生的名字都丢到m的名称空间
    3.在当前文件中产生一个名字m,指向1中产生的名称空间
    
    image-20201211193721713

    [之后导入]

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

    [深入理解]

    # demo.py
    import m
    
    print(m.x)            # 100
    print(m.get)          # <function get at 0x000001FD80567620>
    print(m.change)       # <function change at 0x000001FD81616510>
    

    [强调]

    1. 模块名.名字,是指名道姓的问某一个模块要名字对应的值,不会与当前名称空间的名字发生冲突
    2. 无论是查看还是修改操作的都是以定义阶段为准(原模块本身),与调用位置无关
    # demo.py
    import m
    
    x = 333
    m.get()     # get ======== 100
    
    m.change()  # 改变的是m名称空间里面的x值
    print(x)    # 333
    
    print(m.x)  # 101
    

    [补充]

    # 1.可以以逗号为分隔符在一行导入多个模块
    # 建议如下所示导入多个模块,层次清晰
    import time
    import random
    import m
    
    # 不建议在一行同时导入多个模块
    import time, random, m
    
    # 2.导入模块的规范
    
    # 1.python内置模块
    # 2.第三方模块
    # 3.程序员自定义模块
    
    import time
    import sys
    import 第三方模块1
    import 第三方模块2
    import 自定义模块1
    import 自定义模块1
    
    # 3.import ... as ...
    # 为导入的模块起一个别名,以后可以通过别名.名字,取到模块里面的名字
    # 适用于比较长的第三方模块
    
    import jhcsdfveubvf as f
    
    # 4.模块是第一类对象,和变量名,函数名一样,可以被赋值,可以当参数,返回值,容器的元素
    
    # 5.自定义模块的命名应该采用纯小写+下划线风格,py2中还是驼峰体
    
    # 6.可以在函数中导入模块
    

    2.2 from ... import ....

    # foo.py
    print('======foo=====')
    x = 0
    def get():
        print("get ========%s" % x)
    
    
    def change():
        global x
        x += 1
        return x
    
    # run.py
    
    from foo import X          # x=模块foo中值0的内存地址
    from foo import get
    from foo import change
    
    print(x)                   # 0
    print(get)                 # <function get at 0x0000029FBD8E7620>
    print(change)              # <function change at 0x0000029FBE996510>
    
    # 注意:x,get,change这三个名字是在run的名称空间中,但是指向的都是foo里面值的内存地址,并不是名字
    # 只是他们的名字相同又指向相同的内存地址,但是不在一个名称空间,所以不会相互影响
    
    image-20201212124645778

    [导入模块发生的事]

    # 1、产一个模块的名称空间
    # 2、运行foo.py将运行过程中产生的名字都丢到模块的名称空间去
    # 3、在当前名称空间拿到一个名字,该名字指向模块名称空间中的某一个内存地址
    

    [强调]

    # run.py
    
    from foo import X          # x=模块foo中值0的内存地址
    from foo import get
    from foo import change
    
    print(x)                   # 0
    print(get)                 # <function get at 0x0000029FBD8E7620>
    print(change)              # <function change at 0x0000029FBE996510>
    
    x = 3333       # 改变全局名称空间中x的指向,现在x指向值3333的内存地址
    print(x)       # 3333
    
    get()          # 仍然打印x=0,因为get中的x是引用foo名称空间中x指向的值,foo中x的指向并没有变
    change()       # 改变foo中x的内存地址的指向
    get()          # 打印1
    
    print(x)       # 虽然foo中x的指向发送改变了,但是run中的这个x并不会发生变化,还是原来的内存地址0
    
    from foo import x   # 这个时候x就指向,foo里面x指向的新地址了
    print(x)            # 打印 1
    

    [补充]

    #一行导入多个名字(不推荐)
    from foo import X, get,change
    
    # *:导入模块中的所有名字,当用到一个模块中超级多的名字时,可以用*,但是最好也要规避掉重名的问题
    from foo import *|
    print(x)
    
    # 起别名
    from foo import get as g    # 针对get起了一个别名g
    

    [了解]

    # 了解 : __all__ = ["名字1","名字2",.....],列表的元素全是字符串格式的名字
    # 导入*,实际上就是把列表中的名字全导入进入了,可以通过__all__控制*代表的名字有哪些
    

    2.3 两种方式的比较

    impot导入模块在使用时必须加前缀"模块."
    优点:肯定不会与当前名称空间中的名字冲突
    缺点:加前缀显得麻烦
    
    from.. . impat.. .导入模块在使用时不用加前缀
    优点:代码更精简
    缺点:容易与当前名称空间混淆
    

    3. 循环导入

    极力不推荐在程序中设计有模块的循环导入,但是如果真的遇到,记住两种解决方式

    m1.py

    print("正在导入m1")
    
    from m2 import y
    
    x = 1
    

    m2.py

    print("正在导入m2")
    
    from m1 import x
    
    y = 2
    

    run.py

    import m1
    
    
    # 运行代码,首先会加载m1的名称空间,然后运行里面的代码,当运行到第二句的时候,此时m1的名称空间中还没有x这个名字,由于是首次导入会加载m2的名称空间,运行m2的代码,所以控制台会打印正在导入m1,正在导入m2,当运行当m2的第二句代码时,由于m1不是首次导入,所以不会再加载名称空间,也不会执行代码,但是m2问m1要x这个名字,由于m1中的x这个名字还没有加载到名称空间,所以程序报错
    

    [解决方法]

    # 方式1:
    # 将m1,和m2中的from m2 import y,from m1 import x,放在文件的最后一行,确保导入的时候,名字已经完全加载到名称空间了
    
    # 方式2:
    # 将模块导入的代码,放在函数体中,这样保证在运行代码的时候,函数未调用函数体里面的代码不执行
    

    4. 搜索模块路径的优先级

    无论是 import还是from ...import 在导入模块时都涉及到查找问题

    [优先级]

    1. 内存 (内置模块,默认会自动加载到内存中)
    2. 硬盘 : 按照sys.path(环境变量)中存放的文件的顺序依次查找要导入的模块
    import sys
    print(sys.path)
    
    ['E:\project\python\s29code\day21', 'E:\project\python\s29code', 'E:\developTools\pycharm\PyCharm 2020.1\plugins\python\helpers\pycharm_display', 'E:\developTools\Anaconda3\python36.zip', 'E:\developTools\Anaconda3\DLLs', 'E:\developTools\Anaconda3\lib', 'E:\developTools\Anaconda3', 'E:\developTools\Anaconda3\lib\site-packages', 'E:\developTools\Anaconda3\lib\site-packages\win32', 'E:\developTools\Anaconda3\lib\site-packages\win32\lib', 'E:\developTools\Anaconda3\lib\site-packages\Pythonwin', 'E:\developTools\pycharm\PyCharm 2020.1\plugins\python\helpers\pycharm_matplotlib_backend']
    
    # 其中第一个文件夹是当前执行文件所在的文件夹  E:\project\python\s29code\day21
    # 其中第二个文件夹是当前项目的文件夹  E:\project\python\s29code
    # 这个值是pycharm自动添加的,所以当他不存在,值里面的压缩包就当文件夹看待
    

    [补充]

    通过sys.modules查看已将加载到内存的模块,内容格式 {'模块名':'内存地址'}

    print('foo' in sys.modules)   # 判断foo模块是否在当前内存中
    print('m1' in sys.modules)    # 判断foo模块是否在当前内存中
    
    {'builtins': <module 'builtins' (built-in)>, 'sys': <module 'sys' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>, '_imp': <module '_imp' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_thread': <module '_thread' (built-in)>, '_weakref': <module '_weakref' (built-in)>, '_frozen_importlib_external': <module 'importlib._bootstrap_external' (frozen)>, '_io': <module 'io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'nt': <module 'nt' (built-in)>,...}
    

    [python的优化机制]

    导入模块会发生模块名和对应的内存地址的捆绑关系,但是你通过del 模块名
    是无法从内存中把那块空间回收的,因为python导入一个模块是较耗费内存的
    如果你中途从内存中删除了,再导入就会再开辟内存空间,极大的消耗内存
    同样你在函数内部中导入模块,函数调用结束后,导入的模块在内存中也不会回收
    只有当当前运行文件结束,无其他文件对模块中的名字有引用,模块才会从内存中消失
    

    5. 模块规范写法

    我们在编写py文件时,需要时刻提醒自己,该文件既是给自己用的,也有可能会被其他人使用,

    因而代码的可读性与易维护性显得十分重要,为此我们在编写一个模块时最好按照统一的规范去编写,如下

    "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()  # 在被当做脚本执行时,执行此处的代码
    

    6. py文件的用途

    6.1 当做模块导入

    # foo.py
    print('======m=====')
    x = 0
    def get():
        print("get ========%s" % x)
    
    
    def change():
        global x
        x += 1
        return x
    
    # run.py
    import foo      # 首次导入模块,执行代码
    #print(foo)
    
    print(foo.x)    # 0
    foo.change()    # 把x指向的内存地址改变了,指向了1
    print(foo.x)    # 1
    
    x = 1
    y = 2
    
    print('11111')
    print('22222')
    
    
    image-20201211202054880
    # 名称空间的回收顺序
    首次导入foo模块,foo代码会运行,但是代码运行结束后它的名称空间没有被回收,因为里面的名字被run.py引用了,紧接着run.py中的代码运行完毕后,它的名称空间里面的名字没有被其他引用,运行完后,run.py的名称空间就被回收了,foo的名称空间也被回收了
    
    # 总结: 模块没有被其他文件引用就被回收了
    

    5.2 当做程序运行

    如果一个模块在开发测试的时候,肯定会要运行,但是如果被别人导入的时候,不想执行里面的函数怎么办呢?

    # foo.py
    print('======m=====')
    x = 100
    def get():
        print("get ========%s" % x)
    
    
    def change():
        global x
        x += 1
        return x
    
    if __name__ == '__main__':
        # 当作程序运行的时候做的事情
        get()
        change()
    else:
        # 被当做模块导入的时候做的事情
        pass
    
    # 每一个py文件内部都有一个__name__变量,
    # 当文件当做程序运行的时候,__name__ == __main__
    # 当文件当做模块导入时,__name__ == 模块名,即去掉后缀的文件名
    
  • 相关阅读:
    mysql qps tps
    SQL RIGHT JOIN 关键字
    C#的多态性
    c# 与java之间的简单区别
    多少行转多少列
    SQL语句中&、单引号等特殊符号的处理
    sql 函数的理解
    c# 基础之数组(包含三维数组)
    泛型之Dictionary
    在十六进制字符串与数值类型之间转换
  • 原文地址:https://www.cnblogs.com/xcymn/p/14125283.html
Copyright © 2020-2023  润新知