day21

1. 模块

1.1 什么是模块?
    # 模块是已经写好的一组功能的集合
    # 别人写好的函数 变量 方法 放在一个文件里 (这个文件可以被我们直接使用)这个文件就是一个模块
    # 模块可以是:py文件 dll文件 zip文件

1.2 如何自己写一个模块
    # 创建一个py文件,给它起一个符合变量名命名规则的名字,这个名字就是模块名

 2. 模块的导入

2.1 import 模块名
    假设有在同级目录下有一个my_module.py文件
    my_module.py:
    ------------------------------
        print(12345)

        name = 'alex'

        def read1():
            print('in read1 func')

        def read2():
            print("in read2 func", name)

        print(54321)
    ------------------------------
    # 在导入模块的过程中发生了什么?
        import my_module
        结果: 12345
                54321

    总结:导入一个模块就是执行一个模块

    # 怎么使用my_module模块中的名字
    print(my_module.name)  # alex
    print(my_module.read1)  # <function read1 at 0x0000017D5B7E9510>
    my_module.read1()  # in read1 func

    # improt的命名空间,模块和当前文件在不同的命名空间中
    name = 'egon'
    def read1():
        print("main read1")

    print(name)  # egon
    print(my_module.name)  # alex

    # 模块只能用自己命名空间的变量,不能用当前空间的,因为没有执行关系
    name = 'egon'
    def read1():
        print("main read1")

    print(name)  # egon
    print(my_module.name)  # alex
    my_module.read2()  # in read2 func alex

    总结:每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突
    ---------------------------------------
    # 模块是否可以被重复导入?
    import my_module
    import my_module
    结果: 13245
          54321

    总结:第一次导入后就将模块名加载到内存,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句。

    # 怎么判断这个模块已经被导入过了???
    import sys
    print(sys.modules)

    # 模块导入的过程中发生了什么?
    # 找到这个模块
    # 判断这个模块是否被导入过了
    # 如果没有导入过
        # 创建一个属于这个模块的命名空间
        # 让模块的名字 指向 这个空间
        # 执行这个模块中的代码
    ------------------------------------
    # 给模块起别名,起了别名之后,使用这个模块就都使用别名引用变量了
    import my_module as m  # 12345 54321
    m.read1()  # in read1 func

    # json pickle
    # dumps loads
    def func(dic, t = 'json'):
        if t == 'json':
            import json
            return json.dumps(dic)
        elif t == 'pickle':
            import pickle
            return pickle.dumps(dic)
    # 直接用别名,简化代码
    def func(dic, t = 'json'):
        if t == 'json':
            import json as aaa
        elif t == 'pickle':
            import pickle as aaa
        return aaa.dumps(dic)
    -----------------------------------
    # 导入多个模块/分别起别名
        import os,time
        import os as o,time as t

    # 规范建议 模块应该一个一个的导入: 内置模块,第三方模块,自定义模块

    # 所以导入模块是有顺序的:
        # 内置模块
        # 扩展(第三方模块)
        # 自定义模块

    # 大家都符合这个规范,是为了大家用彼此模块很方便
    # import os

    # import django

    # import my_module
2.2 模块的导入from import
    # 如何使用 from import?
        # 需要从一个文件中使用哪个名字,就把这个名字导入进来
        # from my_module import name

    # from import的过程中仍然会执行了这个被导入的文件
    -------------------------------------------------------------
    # import谁就只能用谁
    from my_module import read1  # 第一次导入,创建命名空间,执行这个文件,找到要导入的变量,创建引用指向要导入的变量.
    read1()  # in read1 func 这里只导入了read1,所以只能用read

    # 想要使用read2,还要再导入read2
    from my_module import read2  # 上面已经导入过,所以直接找到要导入的变量,创建引用指向要导入的变量.
    read2()  # in read2 func alex
    --------------------------------------------------------------------
    # 覆盖问题
    from my_module import read1  # 第一次被导入,创建命名空间,执行,找到变量,创建引用指向要导入的变量
    def read1():  # 这里重新定义了read1,所以导入进来的模块中的read1倍覆盖
        print("in my read1")
    read1()  # in my read1

    from my_module import read2  # 已经被导入过,直接找到变量,创建引用指向要导入的变量
    read2()  # in read2 func alex
    --------------------------------------------------------------
    # 一行导入多个名字?
    from my_module import read1, read2  # 12345 54321
    read1()  # in read1 func
    read2()  # in read2 func alex
    -------------------------------------------------------------------
    # 给导入的名字起别名
    from my_module import read1 as r1, read2 as r2
     read1():
        print("in my read1")
    r1()  # in read1 func
    r2()  # in read2 func alex
    read1()  # in my read1
    ------------------------------------------------------------------
    # from my_module import * 在导入的过程中 内存的引用变化
    from my_module import *  # 导入my_module.py文件中的所有成员
    name = 'egon'  # name被覆盖
    print(name)  # egon
    read1()
    read2()  # 用的是自己空间的alex
    ----------------------------------------------------------------
    # * 和 __all__  __all__能够约束*导入变量的内容
    修改:在my_module.py文件的上面加上一行__all__ = ['name']
    from my_module import *  # 12345 54321
    print(name)  # alex
    read1()  # 报错
    read2()  # 报错

    # __all__ 只能约束*

3. 模块中引用的情况

3.1 模块的循环引用 ☆☆☆
    # 模块之间不允许循环引用
    # 比如以下这些情况:





   模块a和模块b的引用构成了一个循环

3.2 模块的加载与修改 ☆
    ------------------------------------------
    import time  # 导入时间模块
    import my_module  # 导入my_module.py文件,此时read1函数里是打印111
    
    time.sleep(10)  # 程序在这里停止10s,假设我在这里修改了打印为222
    my_module.read1()  # 那么这里打印的还是111,因为导入的时候已经存在于内存当中了,你修改的是文件里面的,你修改不了内存

    总结: 已经被导入的模块发生了修改,是不会被感知到的
    ----------------------------------------------------
    import time  # 导入时间模块
    import importlib
    import my_module  # 导入my_module.py文件,此时read1函数里是打印111

    time.sleep(10)  # # 程序在这里停止10s,假设我在这里修改read1函数打印的为222
    importlib.reload(my_module)  # 重新导入模块my_module,此时内容已经被修改
    my_module.read1()  # 所以此时read1打印的是222

    总结: 要想修改的模块被正在运行中的程序感知到,重启这个程序(或者重新加载模块)
3.3 把模块当初脚本执行 ☆☆☆☆☆
    # 执行一个py文件的方式:
        # 在cmd里执行/在pycharm执行: 直接执行这个文件 - 以脚本的形式运行这个文件
        # 导入这个文件
    
    # 都是py文件
        # 直接运行这个文件 这个文件就是一个脚本
        # 导入这个文件   这个文件就是一个模块

    有calculate.py文件,里面是一个计算器
    -----------------------------------------------
    def main(exp):
        exp = exp.replace(' ', '')
        while 1:
            ret = re.search('([^()]+)', exp)
            if ret:
                inner_backet = ret.group()
                res = str(cal(inner_backet))
                exp = exp.replace(inner_backet, res)
                exp = format_exp(exp)
            else:
                break
        return cal(exp)

    s = input(">>>")
    print(main(s))
    -------------------------------------------------
    假设我直接执行这个文件,此时它是脚本,它可以独立的提供一个功能
    但是假设我从同级目录的文件导入它,此时它是一个模块,能够被导入者调用这个功能,会先执行交互,但是我们不需要这个交互
    
    # 当一个py文件
    # 当做一个脚本的时候: 能够独立的提供一个功能,能自主完成交互
    # 当成一个模块的时候: 能够被导入者调用这个功能,不能自主交互

# 一个文件中的__name__变量
    # 当这个文件被当做脚本执行的时候 __name__ == '__main__'
    # 当这个文件被当前模块导入的时候 __name__ == '模块的名字'

    这时我们可以用__name__ 来完成脚本和模块的需求
    将    if __name__ == '__main__':
               s = input(">>>")
               print(main(s))    放在calculate.py文件的最下面,
    当我们直接执行这个文件时,如果__name__等于'__main__',就可以执行交互,else:当它被当做模块导入时,__name__等于'模块的名字'
    此时calculate.py文件为
    ------------------------------------------
        def main(exp):
        exp = exp.replace(' ', '')
        while 1:
            ret = re.search('([^()]+)', exp)
            if ret:
                inner_backet = ret.group()
                res = str(cal(inner_backet))
                exp = exp.replace(inner_backet, res)
                exp = format_exp(exp)
            else:
                break
        return cal(exp)
    if __name__ == '__main__'
        s = input(">>>")
        print(main(s))
3.4 模块搜索路径 ☆☆☆☆☆
    # 和被当做脚本执行的文件 同目录下的文件 可以直接被导入
        因为被当做脚本直接执行这个文件,模块搜索路径中有这个文件的所在的目录,所有这个文件同级的模块可以被直接导入,因为他们的目录都一样

    # 除此之外其他路径下的模块 在被导入的时候需要自己修改sys.path列表

 4. 包

4.1 什么是包?
    # 包: 文件夹中有一个__init__.py文件
    # 包: 是几个模块的集合

4.2 包的导入语句
    
    # import  从包当中导入模块
    # import glance2.api.policy
    # glance2.api.policy.get()
    
    # import glance2.api.policy as policy
    # policy.get()
    
    # from import  导入模块,或者导入模块中的方法
    # from glance2.api import policy
    # policy.get()
    
    # from glance2.api.policy import get
    # get()

    # 关于包相关的导入语句也分为import和from ... import ...两种,但    是无论哪种,无论在什么位置,
    # 在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。

    # 需要注意的是from后import导入的模块,必须是明确的一个不能带点,
    # 否则会有语法错误,如:from a import b.c是错误语法
4.3 绝对导入/相对导入
    (1)直接导入包
        # 导入一个包
            # 不意味着这个包下面的所有的内容都是可以被使用的
            # 导入一个包到底发生了什么?
            # 相当于执行了这个包下面的__init__.py文件

    (2)绝对导入
        # 绝对导入:
        # 优点:
            # 在执行一个python脚本的时候,这个脚本以及和这个脚本同级的模块只能用绝对导入
        # 缺点:
            # 所有的导入都要从一个根目录下往后解释文件夹之间的关系
            # 是是如果当前导入包的文件和被导入的包的位置发生了变化,那么所有init文件都要做相应的调整

    (3)相对导入
        # 相对导入
        # 优点:
            # 不需要去反复的修改路径
                # 只要一个包中的所有文件夹和文件的相对位置不发生改变
            # 也不需要去关心当前这个包和被执行文件之间的层级关系
        # 缺点:
            # 含有相对导入的py文件不能被直接执行
            # 必须放在包中被导入的调用才能正常执行

    # 如果只是从包中导入模块的话,那么我们不需要做任何多余的操作
    # 直接导入就行了

    # 如果我们希望导入包的时候,能够顺便把模块也导进来
    # 需要设计init文件
    # 绝对目录的导入相对目录的导入各有千秋
原文地址:https://www.cnblogs.com/kangqi452/p/11509070.html