zoukankan      html  css  js  c++  java
  • 模块和包

    一、模块

      1、什么是模块?
        常见的场景: 一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。但其实import加载的模块分为四个通用类别: 
          1 使用python编写的代码(.py文件)
          2 已被编译为共享库或DLL的C或C++扩展
          3 包好一组模块的包
          4 使用C编写并链接到python解释器的内置模块
      2、为什么要使用模块?
        如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。
        随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用。
      3、如何自己写一个模块?

              创建一个py文件,给它起一个符合变量名命名规则的名字,这个名字就是模块名。如下示例:

        # my_module.py
        print(12345)
    
        name = 'alex'
    
        def read1():
            print('hello world')
    
        def read2():
            print('in read1 func',name)
    
        print(54321)
        注:下文提到的my_module模块就是此示例内容。
    my_module.py

    二、导入模块

      1、import导入模块

        正如我们之前所见,可以通过import导入模块,比如import my_module

           2、怎么使用my_module模块中的名字?

                  print(my_module.name)

                  my_module.read1()

           3、import的命名空间

                  模块和当前文件在不同的命名空间中。

      4、在导入模块的过程中发生了什么?

        导入一个模块就是执行一个模块。

        模块导入的过程如下:

        # 找到这个模块
        # 判断这个模块是否被导入过了
        # 如果没有被导入过
              # 创建一个属于这个模块的命名空间
              # 让模块的名字 指向 这个空间
              # 执行这个模块中的代码

      5、模块是否可以被重复导入?怎么判断一个模块已经被导入过了?

                  import sys

                  print(sys.module)

                  模块的导入相当于执行这个模块所在的文件,模块的多次导入不会重复执行。

      6、给模块起别名(起了别名后,使用这个模块就都使用别名引用变量了)

                  import my_module as m

                  m.read1()   # 使用别名引用,而不能用my_module.read1()了

                  给模块起别名示例(当需要导入不同模块,且导入不同模块所做的操作相同,可以给模块起别名):

          def func(dic, t='json'):
              if t == 'json':
                  import json as aaa
              elif t == 'pickle':
                  import pickle as aaa
              return aaa.dumps(dic)
                

      7、导入多个模块

                  多行导入:

                  import os

                  import time

                  一行导入:

                  import os,time   或者   import os as o,time as t

                  规范建议:模块应该一个一个的导入,顺序为:内置模块、扩展(第三方)模块、自定义模块,如下格式:

        import os
    
        import django
    
        import my_module

      8、如何使用from import

                  from my_module import read2

                  read2()

                  结论:import谁就只能用谁。

      9、当前文件命名空间和模块的命名空间的问题,有如下代码,分析结果:

       from my_module import read1   # 此时执行my_module模块
        def read1():
            print('in my read1')
        read1()
    
        from my_module import read2   # 此时不再执行my_module模块
        read2()
        # 结果为:
        # 12345
        # 54321
        # in my read1
        # in read1 func alex
       分析:from import导入的过程:
            # 1.找到要被导入的模块
            # 2.判断这个模块是否被导入过
            # 3.如果这个模块没被导入过
                # 创建一个属于这个模块的命名空间
                # 执行这个文件
                # 找到你要导入的变量
                # 给你要导入的变量创建一个引用,指向要导入的变量        

      10、导入多个名字、给导入的名字起别名

        from my_module import read1 as r1,read2 as r2
        def read1():
            print('in my read1')
        r1() # 模块中的read1
        r2() # 模块中的read2
        read1() # 本文件中的read1

      11、from my_module import *(代表导入这个模块中的所有名字),使用如下:

        name = 'egon'
        print(name)   # 结果为:egon,模块中的name被本文件的 name='egon'所覆盖
        read1  # 模块中的read1
        read2  # 模块中的read2

        注意:*和__all__的关系:*会参考__all__中的变量,如果没有__all__变量,则导入所有名字,当有__all__变量时,则__all__能够约束*导入的变量的内容,且*里边变量只能是字符串,如下示例:

                         __all__ = [‘name’]

    三、模块引用中的情况

           1、模块的循环引用(***)

                  模块之间不能形成循环引用,否则就会报错。

           2、模块的加载与修改(*)

                  已经被导入的模块发生了修改,是不会被正在运行的程序感知到的,可以用importlib.reload(my_module)重新加载这个模块被感知,但是一般不用,知道即可;

                  所以要想修改的模块被正在运行中的程序感知到,可以重启这个程序,而不是重新加载这个模块;

           3、把模块当成脚本执行(*****)

                  首先要知道,同一个py文件,直接运行这个文件,这个文件就是一个脚本,而导入这个文件,这个文件就是一个模块。

                  其次,当一个py文件被当做脚本执行时,它的__name__ ='__main__';当一个模块被调用时,它的__name__ ='模块的名字'

                  最后,当一个py文件被当做一个脚本的时候,能够独立地提供一个功能,能自主完成交互;当成一个模块的时候,能够被导入者调用这个功能,不能自主交互。

                  所以,可以按照如下这样写:

        # if __name__ == '__main__':
              # 代码
        # 写在这里面的代码只有这个文件被当做脚本执行的时候才执行

      4、模块的搜索路径(*****)

                  被当做脚本执行的文件 的 同目录下的模块,可以被直接导入,除此之外其他路径下的模块,在被导入的时候需要自己修改sys.path列表。

    四、包

           什么是包:文件夹中有一个__init__.py文件,是几个模块的集合。如下示例是一个包的目录结构:

        # glance/
        # ├── __init__.py
        # ├── api
        # │   ├── __init__.py
        # │   ├── policy.py
        # │   └── versions.py
        # ├── cmd
        # │   ├── __init__.py
        # │   └── manage.py
        # └── db
        #     ├── __init__.py
        #     └── models.py

      1、从包中导入模块(精确到模块,不需要设计__init__.py文件)(必须掌握,很简单)

        # 1.1  import方式
        import glance.api.policy   # 导入模块
        glance.api.policy.get()   # 使用模块中的方法
    
        import glance.api.policy as policy  # 导入模块,并给模块起别名
        policy.get()  # 使用模块中的方法
    
        # 1.2  form import方式
        from glance.api import policy
        policy.get()
    
        from glance.api.policy import get
        get()

      总结:1)从包中引入模块的上述两种方式,无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带一连串的点,但都必须遵循这个原则。

           2)from import方式中,import后边必须不能带点,否则有语法错误。

    2、导入包

                  直接导入包:import glance

                  但是导入一个包并不意味包下面的所有内容都是可以被使用的,即直接导入包什么都干不了,那么导入一个包到底发生了什么?导入一个包相当于执行了这个包下面的__init__.py文件

                  所以直接导入包时,我们要通过设计__init__.py文件,来完成导入包之后的操作,这时又分为下面两种方式;绝对导入和相对导入:

      2.1  绝对导入

        # glance/
        # ├── __init__.py    (from glance import api)from glance import cmd)
                    (from glance import db)
        # ├── api
        # │   ├── __init__.py   (from glance.api import policy)from glance.api import version)
        # │   ├── policy.py
        # │   └── versions.py
        # ├── cmd
        # │   ├── __init__.py   (from glance.cmd import manage)
        # │   └── manage.py
        # └── db
        #     ├── __init__.py   (from glance.db import models)
        #     └── models.py
    绝对导入

      绝对导入的缺点:1)所有的导入都要从一个根目录下往后解释文件夹之间的关系;

              2)如果当前导入包的文件和被导入的包的位置关系发生了变化,那么所有的init文件都要做相应的调整;

      2.2  相对导入

        # glance/
        # ├── __init__.py    (from . import api)from . import cmd)
                (from . import db)
        # ├── api
        # │   ├── __init__.py   (from . import policy)from . import version)
        # │   ├── policy.py
        # │   └── versions.py
        # ├── cmd
        # │   ├── __init__.py   (from . import manage)
        # │   └── manage.py     (from ..api import policy)
        # └── db
        #     ├── __init__.py   (from . import models)
        #     └── models.py
    相对导入

      相对导入的优点:1)不需要反复去修改路径,只要一个包中的所有文件夹和文件的相对位置不发生改变;

              2)也不需要关心当前这个包和被执行的文件之间的层级关系;

      注意:含有相对导入的py文件不能被直接执行,且必须放在包中被导入的调用才能正常的使用。所以,在执行一个py脚本的时候,这个脚本以及和这个脚本同级的模块中只能用绝对导入。

      我们再看下面一个现象:

        import urllib
        urllib.request()  # 报错:AttributeError: module 'urllib' has no     attribute 'request'
    
        # from urllib import request
        # print(request)  # <module 'urllib.request' from     'C:\Python36\lib\urllib\request.py'>
        分析:urllib是一个包,单纯的导入一个包啥也不会发生,包中的request.py也不能用。

      总结:如果只是从包中导入模块的话,那么我们不需要做任何多余的操作,直接导入就行了。如果我们希望导入包的时候,能够顺便把模块也导入进来,就需要设计_init__文件,这时有上述绝对目录的设计和相对目录的设计,而且两种方式各有千秋。

    五、项目开发规范

      # myProject/
      # ├── __init__.py    
      # ├── bin   (当前项目的启动脚本在这里)
      # │   ├── __init__.py   
      # │   ├── start.py
      # ├── core   (核心代码都在这里)
      # │   ├── __init__.py   
      # │   └── main.py
      # └── db
      #     ├── __init__.py   
      #     └── userInfo.json
      # └── lib(不是内置和第三方模块,可能是自己写的,较完善且跟此程序相关性小)
      #     ├── __init__.py   
      #     └── models.py
      # └── conf(配置文件,多处用到某个值以后可能会被修改,则写到配置文件中)
      #     ├── __init__.py   
      #     └── config.py
      # └── log(日志文件,供用户查看追责或者公司分析数据)
      #     ├── __init__.py   
      #     └── all.log
  • 相关阅读:
    文件操作回顾
    数据类型回顾
    面向对象三大特性之封装与多态
    面向对象之继承
    轮播图
    jQuery事件操作
    jQuery动画效果
    jQuery篇
    文档对象模型
    javascript
  • 原文地址:https://www.cnblogs.com/li-li/p/9526636.html
Copyright © 2011-2022 走看看