Python基础13_模块, import,

 一. 模块
    模块就是一个包含了python定义和声明的文件, 文件名就是模块的名字加上.py后缀, 也就是我们目前写的所有py文件都可以看成是一个模块, 但是我们import加载的模块一共分为四个通用类别:
    1. 使用python编写的py文件
    2. 已被编译为共享库或者DLL或C或者C++的扩展
    3. 包好一组模块的包
    4. 使用c编写并连接到python解释器的内置模块
    模块的作用: 大的项目需要把相关的功能进行分离, 方便我们的日常维护, 以及新项目的开发
二. import
    import 模块名
    调用: 模块名.方法名()

    在python中模块是不能重复导入的, 当重复导入时, 系统会根据sys.modules来判断该模块是否已经导入了, 如果已经导入了, 则不会重复导入
    sys.modules.keys()    查看导入的模块    详情: https://www.cnblogs.com/zhaojingyu/p/9069076.html
    导入模块都做了些什么, 首先, 在导入模块的一瞬间, python解释器会先通过sys.modules来判断该模块是否已导入了该模块, 如果已经导入了, 则不再导入, 如果该模块还未导入过, 则系统会做三件事:
    1. 为导入的模块创立新的名称空间
    2. 在创建的名称空间中运行该模块中的代码
    3. 创建模块的名字, 并使用该名称作为该模块在当前模块中引用的名字
    我们可以使用globals来查看模块的名称空间
    注意: 由于模块在导入的时候会创建自己的名称空间, 所以, 我们在使用模块中的变量的时候一般是不会产生冲突的,  在模块中使用global, 我们之前说global表示把全局的内容引入到局部, 但是这个全局指的是py文件, 换句话说, global指向的是模块内部, 并不会改变外部模块的内容
    特别注意: 如果我们在不同的模块中引入了同一个模块, 并且在某一个模块中改变了被引入模块中的全局变量, 则其他模块看到的值也跟着变, 原因是python的模块只会引入一次, 大家共享同一个名称空间
    
    在python中, 每一个模块都有自己的__name__, 但是这个__name__的值是"__main__", 而如果我们把模块导入时, 此时模块内部的__name__就是该模块自身的名字
    我们可以根据这个特性来控制模块内哪些代码是在被加载的时候就运行的, 哪些是在模块被别人导入的时候就要执行的, 也可以屏蔽掉一些不希望别人导入就运行的代码, 尤其是测试代码 
    我还可以对导入的模块进行命名, 就像变量赋值一样
    import 模块名 as 变量名
    所有模块的导入都要写在上面, 导入多个模块的顺序:
    1. 先引入内置模块
    2. 再引入扩展模块
    3. 最后引入自定义模块
    坑: 从模块中引入一个变量的时候, 如果当前文件中出现了重名的变量时, 会覆盖掉模块引入的那个变量
三. from 模块名 import 方法名
    部分导入, 当一个模块的内容过多的时候, 我们就可以选择性的导入要使用的内容
    调用: 方法名()
    
    切记: 不要重名, 我们创建的py文件的名字不要和系统内置的模块重名, 否则引入的模块都是python内置的模块
四. 补充
    from xxx import *: 把模块中所有的内容都导入, 注意, 如果模块中没有写出__all__, 则默认所有的内容都导入了, 如果重写了__all__, 此时导入的内容就是在__all__列表中列出来的所有名字
五. 包    详情: https://www.cnblogs.com/kex1n/p/5977051.html
    包是一种通过"模块名"来组织python模块名称空间的方式, 我们创建的每个文件夹都可以称之为包, 
    python2中规定, 包内必须存在__init__.py文件, 创建包的目的不是为了运行, 而是被导入使用, 包只是一种形式而已, 包的本质就是一种模块, 提高程序的结构性和可维护性
    创建包:
    import os
    os.makedirs('glance/api')    # 创建文件夹
    os.makedirs('glance/cmd')
    os.makedirs('glance/db')
    l = []
    l.append(open('glance/__init__.py','w'))        # 创建py文件
    l.append(open('glance/api/__init__.py','w'))
    l.append(open('glance/api/policy.py','w'))
    l.append(open('glance/api/versions.py','w'))
    l.append(open('glance/cmd/__init__.py','w'))
    l.append(open('glance/cmd/manage.py','w'))
    l.append(open('glance/db/__init__.py','w'))
    l.append(open('glance/db/models.py','w'))
    map(lambda f:f.close() ,l)                        # map 映射 逐个关闭    
    __init__.py: 不论我们使用哪种方式导入一个包, 只要是第一次导入包或者是包的其他任何部分, 都会先执行__init__.py文件, 这个文件可以是空的, 但也可以存放一些初始化的代码(随意在glance中的__init__.py里都可以进行测试)
    绝对导入和相对导入:
    1. 绝对导入: 以glance作为起始
    2. 相对导入: 用.或者..作为起始
    举例: 在glance/api/version.py中使用glance/cmd/manage.py中的main()
    # 在versions.py文件中
    绝对导入:
    from glance.cmd import manage
    manage.main()
    # 绝对导入没毛病
    相对导入:
    from .. cmd import manage
    manage.main()
    # 相对导入, 如果在version中运行的话会报错
    # ValueError: attempted relative import beyond top-level package 试图在顶级包之外进行相对导入
    测试时要注意, python包路径跟运行脚本所在的目录有关系, 就是你运行的py文件所在的目录, 在python中不允许你运行的程序导包的时候超过当前包的范围(相对导包), 如果使用绝对导包, 没有这个问题, 换种说法, 如果你在包内使用了相对导入, name使用该包内信息的时候只能在包外面导入
    再举例: 在policy中使用version中的内容
    # 在policy.py中
    import versions
    如果我们程序的入口是policy.py, 那此时程序时没有问题的, 但是如果我们在glance外面import了glance中的policy就会报错, 原因是如果在外面访问policy的时候, sys.path中的路径就在外面, 所以根本就不能直接找到versions模块, 所以一定报错
    在导包出错的时候, 一定要先看sys.path看一下是否真的能获取到包的信息
    
    包的注意事项:
    1. 关于包的相关导入语句也分为import和from xxx import xxx两种, 但无论使用哪种, 无论在什么位置, 在导入时都必须遵循一个原则: 凡是在导入时带.的, 点左边必须是一个包, 否则报错, 可以带一连串的点
    2. import导入文件时, 产生名称空间的名字来源于文件, import包, 产生的名称空间中的名字同样来源于文件, 即包下的__init__.py, 导入包本质就是在导入该文件
    3. 包A和包B下有同名模块也不会冲突, 如A.a和B.b来自两个名称空间

猜你喜欢

转载自www.cnblogs.com/guyannanfei/p/10197725.html