zoukankan      html  css  js  c++  java
  • python 的模块与包

    一 模块

    1、什么是模块

    模块是一系列功能的结合体

    ​ 分为三大类:

    ​ 1、内置的模块

    ​ 2、第三方的模块

    ​ 3、自定义的模块

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

    ps:模块分为四种形式

    ​ 1 使用python编写的.py文件

      2 已被编译为共享库或DLL的C或C++扩展

      3 把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)

      4 使用C编写并链接到python解释器的内置模块

    2、为何要用模块

    1、内置与第三方的模块拿来就用,无需定义,这种拿来主义,可以极大地提升自己的开发效率

    2、自定义模块,可以将程序的各部分功能提取出来放到一模块中为大家共享使用

    ​ 好处:减少代码冗余,程序组织结构更加清晰

    3、如何使用模块

    1 导入模块

    image-20200327111216285

    首次导入模块会发生的事情

    import foo
    

    1、产生 foo.py 的名称空间, 将 foo.py运行过程中产生的名字都丢到 foo 的名称空间中

    2、执行 foo.py

    3、在当前文件中产生的有一个名字 foo,该名字指向 1 中产生的名称空间

    具体如下图:

    image-20200326122226580

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

    import foo
    import foo
    import foo
    

    2 引用

    具体:

    print(foo.x)
    print(foo.get)
    print(foo.charge)
    

    强调1:模块名.名字,是指名道姓地问某一个模块要名字对应的值,不会与当前名称空间中的名字发生冲突

    x=1111111111111
    print(x)
    print(foo.x)
    

    强调 2:无论是查看还是修改,操作的都是模块本身,与调用位置无关

    import foo
    
    x=3333333333
    # foo.get()
    
    foo.change()
    print(x)
    

    可以以逗号为分隔符在一行导入多个模块

    import time, foo, m
    

    3、导入模块的规范(顺序)

    ​ 1、内置模块

    ​ 2、第三方模块

    ​ 3、程序员自定义模块

    模块是第一类对象

    imoport foo
    

    自定义模块的命名应该采用纯小写 + 下划线的风格

    可以在函数内导入模块

    def func():
        import foo
    

    4、 py 文件的用途

    ​ 一个 py 文件有两种用途

    ​ 1)被当做程序运行

    ​ 2)被当做模块导入

    1、执行 py 文件与导入文件的区别是什么?

    ​ 执行 py 文件:

    ​ 1)在内存中产生名称空间

    ​ 2)运行 py 文件

    ​ 3)将运行 py 文件产生的名字放到名称空间内

    ​ 名称空间会在文件运行结束后回收

    ​ 导入文件:

    ​ 1)产生模块文件的内存空间

    ​ 2)运行模块

    ​ 3)在该文件名称空间内产生模块字并指向模块文件的名称空间

    2、from...import.....

    ​ 1、产生一个模块的名称空间

    ​ 2、运行 foo.py 将运行过程中产生的名字都丢到模块的名称空间去

    ​ 3、在当前名称空间拿到一个名字,该名字指向模块名称空间 中的某一个内存地址

    image-20200327210340523
    from foo import x #x = 模块foo中的值1的内存地址
    from foo improt change
    from foo import get
    x = 111 #重新将 x 赋值 指向新的内存地址
    
    

    ​ 优点:代码更精简,导入后在使用时不需要加前缀

    ​ 缺点: 容易与当前名称空间混淆

    一行导入多个名字(不推荐)

    from foo import x,get,change
    
    

    导入模块中所有名字(不推荐)

    from foo import *
    
    

    了解:

    __ all __:在模块中默认存的是模块中所有名字,所以在其它地方能通过 from foo import * 导入所有的名字

    __ all__ =['login', 'get'] #控制*代表的名字有哪些
    
    

    起别名

    from foo improt x as xx
    
    
    3、循坏导入问题

    循环导入问题指的是在一个模块加载/导入的过程中导入另外一个模块,而在另外一个模块中又返回来调用第一个模块中名字,由于第一个模块尚未加载完毕,所以引用失败,抛出异常。原因是:在 python 中,同一个模块只会在第一次导入时执行其内部代码,再次导入该模块时,即便是该模块尚未完全加载完毕也不会重复执行内部代码。

    具体案列:

    m1.py

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

    m2.py

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

    run.py

    import m1
    
    #1、执行run.py会抛出异常
    正在导入m1
    正在导入m2
    Traceback (most recent call last):
      File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module>
        import m1
      File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
        from m2 import y
      File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
        from m1 import x
    ImportError: cannot import name 'x'
    
    #2、分析
    先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
    --->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错
    
    

    解决方案

    方案一:

    ​ 将导入模块放到最后,保证在导入时,所有名字都已经加载过

    # 文件:m1.py
    print('正在导入m1')
    
    x='m1'
    
    from m2 import y
    
    # 文件:m2.py
    print('正在导入m2')
    y='m2'
    
    from m1 import x
    
    # 文件:run.py内容如下,执行该文件,可以正常使用
    import m1
    print(m1.x)
    print(m1.y)
    
    

    方案二:

    导入语句放到函数中,只有调用函数才会执行其内部代码

    # 文件:m1.py
    print('正在导入m1')
    
    def f1():
        from m2 import y
        print(x,y)
    
    x = 'm1'
    
    # 文件:m2.py
    print('正在导入m2')
    
    def f2():
        from m1 import x
        print(x,y)
    
    y = 'm2'
    
    # 文件:run.py内容如下,执行该文件,可以正常使用
    import m1
    
    m1.f1()
    
    

    注:循坏导入问题大多数情况下是因为程序设计失误导致的,在编写程序时应该尽量避免出现循环/嵌套导入。如果多个模块都需要共享某些数据,可以将共享的数据集中存放到某一个地方。

    4、搜索模块的路径与优先级

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

    优先级:

    ​ 1、内存(内置模块)

    ​ 2、硬盘:按照 sys.path中存放的文件的顺序依次查找要导入的模块

    # mmm.py   #它是代码文件夹下的py 文件
    import sys
    print(sys.path) 
     
    #环境变量路径
    ['/Users/tophan/Desktop/代码', '/Users/tophan/Desktop/代码', '/Applications/PyCharm.app/Contents/helpers/pycharm_display', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Users/tophan/Library/Python/3.6/lib/python/site-packages', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend']
    
    1、列表中的每一个元素都可以看做一个文件目录
    2、列表中第一个目录为执行文件的路径
    3、第二个目录为整个项目的路径
    
    
    

    当第一次导入模块时,如果被导入模块与执行文件在同一目录下是肯定可以正常导入的,如果不在同一目录下,就涉遍历查找该模块的存放目录是否在环境变量路径中。为了确保模块对应的源文件在任何地方都可以被找到,需要将源文件所在的路径添加到 sys.path中。

    #sys.append(模块源文件的路径)
    
    sys.path.append(r'/pythoner/projects/') #也可以使用sys.path.insert(……)
    
    import foo #无论foo.py在何处,都可以导入它
    
    

    5、区分py文件

    区分一个 py 文件是执行文件还是导入文件

    print(__name__)
    
    1、当 py 文件被运行时,__name__的值为'__mian__'
    2、当 py 文件被当做模块导入时,__name__的值为模块名
    
    if __ name__ == '__main__':
      	print('文件被执行')
        get()
        change()
    else:
      	#被当做模块导入时做的事情
        print('文件被导入')
        
    
    

    二 包

    1、包就是一个包含有__ init __ .py文件的文件夹

    ​ 包的本质是模块的一种形式,包是用来被当做模块导入,导入包其实其实是导入包下的__ init __文件

    1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错

    2. 创建包的目的不是为了运行,而是被导入使用,包只是模块的一种形式而已,包的本质就是一种模块

    导入包会发生的事情:

    1、产生一个名称空间

    2、运行包下的__ init __.py 文件,将运行过程中产生的名字都 丢到 1(模块)产生的名称空间中

    3、在当前执行文件的名称空间中拿到一个名字,这个名字指向 1 (模块)的名称空间

    import mmm
    print(mmm.x)
    print(mmm.y)
    mmm.say()
    
    

    环境变量是以执行文件为准的,所有被导入的模块或者说后续的其他文件引用的 sys.path都是参照执行文件的 sys.path

    也就是说,一个项目在执行文件运行时,项目中其他的文件的模块或包被导入和引用时都是去执行文件的 sys.path查找的,被导入模块中有 sys.path.append('路径'),在执行文件运行时,被导入的模块会将路径添加到环境变量列表中。

    #模块:mmm
    print('运行了。。。。')
    import sys
    sys.path.append('/11111111111')
    
    
    #执行文件
    import sys
    
    import mmm
    sys.path.append('/aaaaaaaaaaaaaaaaaaaaaaaaaa')
    print(sys.path)
    
    结果展示:
    运行了。。。。
    ['/Users/tophan/Desktop/代码', '/Users/tophan/Desktop/代码', '/Applications/PyCharm.app/Contents/helpers/pycharm_display', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload', '/Users/tophan/Library/Python/3.6/lib/python/site-packages', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend', '/11111111111', '/aaaaaaaaaaaaaaaaaaaaaaaaaa']
    
    #被导入模块中有 sys.path.append('路径'),在执行文件运行时,被导入的模块会将路径添加到环境变量列表中。
    
    
    

    2 导入包 和 模块遵循原则

    1、凡是在导入时带点的,点的左边都必须是一个包,否则非法。

    2、可以带有一连串的点,如import 顶级包.子包.子模块,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。

    from a.b.c.d.e.f import xxx
    import a.b.c.d.e.f
    #其中a、b、c、d、e 都必须是包
    
    

    导入包:

    1、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间

    2、import 导入文件时,产生名称空间中的名字来源文件

    ​ import 包,产生的名称空间的名字同样来源于文件,即包下的__ init __ .py , 导入包本质就是导入该文件。

    3、导入包的两种方式

    1、绝对导入

    ​ 绝对导入,以包的文件夹作为起始来进行导入

    import sys
    
    from foo.m3 import f3
    
    

    2、相对导入

    ​ 相对导入:仅限于包内使用,不能跨出包(包内模块之间的导入,推荐使用相对导入)

    . :代码当前文件夹

    .. : 代表上一层文件夹

    #目录
    foo--|
    		bbb
         |
    		m1
         |
        m2
    		 |
        foo1
        
    # 在foo1执行文件中
    from .m1 import f1
    from .m2 import f2
    from .bbb.m4 import f4
    
    

    强调:

    ​ 1、相对导入不能跨出包,所以相对导入仅限于包内模板彼此之间应用

    ​ 2、绝对导入时没有任何限制的,所以绝对导入是一种通用的导入方式

    补充:

     在pycharm中导入自己写的模块时,得不到智能提示,并在模块名下出现下红线,但是代码可以执行,错误提示为下图所示:

    image-20200327235803829

    原因:出现 以上情况,是因为文件目录设置的问题,pycharm中的最上层文件夹是项目文件夹,在项目中导包默认是从这个目录下寻找,当在其中再次建立目录,目录内的py文件如果要导入当前目录内的其他文件,单纯的使用import导入,是得不到智能提示的,这是pycharm设置的问题,并非导入错误。

    解决方法:

    ​ 从根目录下导入时,就会有提示信息

    from ATM.lib.common import logger
    
    
  • 相关阅读:
    原码、补码、反码
    处理器体系结构
    CSAPP学习笔记—虚拟内存
    Sequence Models
    Neural Networks and Deep Learning
    windows7_下Eclipse中部署tomcat7.0进行JSP+servlet开发
    used in key specification without a key length
    在jsp页面下, 让eclipse完全支持HTML/JS/CSS智能提示
    求知若饥,虚心若愚
    C指针右左法则
  • 原文地址:https://www.cnblogs.com/xy-han/p/12577463.html
Copyright © 2011-2022 走看看