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

    一、模块

    1. 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀,但其实import加载的模块分为四个通用类别:

      • 使用python编写的代码(.py文件)
      • 已被编译为共享或DLL的C或C++扩展
      • 包好一组模块的包
      • 使用C编写并链接到python解释器的内置函数
    2. 如果你退出Python解释器然后再进入,那么你之前定义的函数或变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python 文件.py ,此时执行的文件就叫做script。当文件脚本很多时,我们将程序分成一个个文件,这样程序的结构就更清晰,方便管理,这时我们不仅仅可以把这些文件当成脚本文件,还可以把他们当作模块来导入到其他模块,实现功能的重用

    3. 使用模块: 我们可以从sys.modules中找到当前已经加载的模块

      • 模块的导入相当于执行了整个文件

      • 一个模块不能被多次导入,一旦导入原文件中的修改也不会生效,且各个模块的变量于本模块不冲突

         
         
         
        x
         
         
         
         
        #测试一:money与my_module.money不冲突
        import my_module  # 默认有以下属性
        money=10
        print(my_module.money) #属性和方法都可以my_moudle.方法()
        my_moudle.read()
        '''
        执行结果:
        from the my_module.py
        1000
        '''
        import my_moudle  # 次文件中有print('123')
        import my_moudle
        # 打印一个 123
         
      • 一个模块不会被多次执行,只执行一遍,再两个模块相互调用时要分清。:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载大内存中的模块对象增加了一次引用,不会重新执行模块内的语句。

      • 导入一个模块的时候命名空间的变化:

        • 创建了一个要导入模块的命名空间
        • 创建一个变量指向这个命名空间,其实相当于加载文件类(个人理解)
        • 执行这个文件,注意这个关系是单向引用,也就是说,如果不是函数传参的形式,是不能把本文件的变量a传给模块中的变量a
      • 模块虽然一行可以导入多个,但是不推荐这样使用。如:import time,os,random,my_module

      • as语法的使用:

         
         
         
        xxxxxxxxxx
         
         
         
         
        #1、time这个名字就失效的,只剩下t了
        import time as t
        t.time()
        #2、用来做兼容,当要根据判断取模块时,我们可以先判断,再以同样的as名字
        mode = 'pickle'  #不确定是 json还是 pickle
        if mode == 'pickle':
            import pickle as mode
        else:
            import json as mode
        def dump():
            mode.dump(obj,f)
        def load():
            mode.load(f)
          
        #其他用法:
        #1
        from my_module import read1 as read
        #2
        from my_module import (read1,
                               read2,
                               money)
         
      • from …… import ……from 语句相当于import,也会创建新的名称空间,但是将my_module中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了

         
         
         
        xxxxxxxxxx
         
         
         
         
        #1测试一:导入的函数read1,执行时仍然回到my_module.py中寻找全局变量money
        from my_module import read1
        money=1000
        read1()
        '''
        执行结果:
        from the my_module.py
        spam->read1->money 1000
        '''
        #测试二:导入的函数read2,执行时需要调用read1(),仍然回到my_module.py中找read1()
        from my_module import read2
        def read1():
            print('==========')
        read2()
        '''
        执行结果:
        from the my_module.py
        my_module->read2 calling read1
        my_module->read1->money 1000
        '''
        #测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
        from my_module import read1
        def read1():
            print('==========')
        read1()
        '''
        执行结果:
        from the my_module.py
        ==========
        ''' 
        #from my_module import * 把my_module中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题
        #在my_module.py中新增一行:
        __all__=['money','read1'] 
        #这样在另外一个文件中用from my_module import *就这能导入列表中规定的两个名字
                           #__all__=[]  和 * 配合使用
        #补充:
        #如果my_module.py中的名字前加_,即_money,则from my_module import *,则_money不能被导入
         

    二、模块导入的三大问题

    • 模块的搜索路径:

      • python解释器在启动时会自动加载一些模块,可以使用sys.modules查看,在第一次导入某个模块时(比如my_module),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用.如果没有,解释器则会查找同名的内建模块,如果还没有找到就从sys.path给出的目录列表中依次寻找my_module.py文件。

         
         
         
        xxxxxxxxxx
         
         
         
         
        #1、所以总结模块的查找顺序是:内存中已经加载的模块 -> 内置模块 -> ys.path路径中包含的模块
        #2、需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名
        #3、搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找,sys.path中还可能包含.zip归档文件和.egg文件,python会把.zip归档文件当成一个目录去处理。在pycharm中会自动为我们加载本地路径
        import sys
        sys.path.append('/a/b/c/d')
        sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索
         
    • 模块能不能被循环导入:

    •  
       
       
      xxxxxxxxxx
       
       
       
       
      #能不能 在 a.py import b
             #在 b.py import a
      # 以主程序为主,加载顺序从上至下,可引用
       
    • 把模块当做脚本执行:

      • 当做脚本运行:__name__ 等于'__main__'__name__ 等于模块名

         
         
         
        xxxxxxxxxx
         
         
         
         
        def fib(n):   
            a, b = 0, 1
            while b < n:
                print(b, end=' ')
                a, b = b, a+b
            print()
        if __name__ == "__main__":  #只有在当前文件下执行,如果是模块调用则不会执行
            print(__name__)
            num = input('num :')
            fib(int(num))
         

    三、包

    • 包是一种通过使用'模块名'来组织python模块名称空间的方式。

      • 无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法

      • 包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录

      • import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件

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

      • 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块

      • import 包:相当于执行了这个包下的init文件

      • 具体使用:

         
         
         
        xxxxxxxxxx
         
         
         
         
        import glance.api.policy as policy  #假设glance是包,api是文件,policy是py文件 用别名
        policy.get() 
        #也可以:import glance.api.policy.get() 但是太长了,每个都要写
        # 根据包的导入要精确到模块名,不能精确到具体的函数或者变量,然后使用glance.api.policy或者重命名的方式,来使用这个模块中的所有名字
        #from……import 在包中的用法
        from glance.api import policy  #import后面不能有. 且至少要精确到模块
        policy.get()
        #or
        from glance.api.policy import get
        get()
        #使用from……import import后面至少是精确到模块的,import后面不能有。from后面可以有.,但是.的左边永远是包名
            
        #import 直接导入包时,再导入时执行了包中的__init__.py文件(类似于实例化),我们可以在__init__.py中写相应的路由,假如我想直接import 一个包,就需要配置__init__.py文件:
          # 1、现在glance(包)中的__init__.py,需要配置:
            
         
      • __init__.py文件:不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的init.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码

      • 首先要想使用import glance 里面的如api下的policy中的方法,先配置glance文件下的__init__.py

         
         
         
        xxxxxxxxxx
         
         
         
         
        # /glance/__init__.py
        from glance import api
        from glance import cmd
        from glance import db  # 此时在包外一个文件import glance,可以调用api,cmd,db,但是还不能导入其中的具体方法
        #test/my_moudle
        import glance
        print(glance)  # <moudle 'glance' from 'D:\'>
        print(glance.api)   #<moudle 'glance.api' from 'D:……>
        print(glance.api.policy)  #不行 如何解决,因为没有在api文件的init中配置路径
        #此时在api/__init__.py中输入
        from api import policy  # 之后再运行还是报错,问题出在from api这里,找不到api,这时候可以通过sys.path 查看路径,显示只能查看能找到glance,也就是说能执行glance中的__init__.py文件,但是不能执行api中的init文件,所以这里有两种方法解决这个问题:
        #方案一:  在api文件中的__init.py文件中添加路径
        import sys
        sys.path.append(r'D:……glance') #将glace文件加载到系统路径中,于是会查找其中的各个文件
        from api import policy  # 导入glance后,api文件就会被找到,一层一层的
        from api import versions 
         #此时api文件下的文件都可以使用了,但是如果要导入cmd文件下的文件,就还需要在它文件下的init文件做同样的处理。麻烦
            
        #方案二:绝对路径,不需要添加路经,使用绝对导入、
        #/glance/api/__init__.py
        from glance.api import policy  #效果相同,不需要让api的上一级成为环境变量中
        from glance.api import versions
        #总结: 在包中的__init__.py文件中的就是在import时要执行的文件,各级子文件的__init__.py文件最好使用绝对导入,这样,想导入的时候才能找到
         

    五、绝对导入和相对导入

    • 绝对导入:以glance作为起始

      相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)

       
       
       
      xxxxxxxxxx
       
       
       
       
      #在glance/api/version.py
      #绝对导入
      from glance.cmd import manage
      manage.main()
      #相对导入
      from ..cmd import manage
      manage.main()
      #可以用import导入内置或者第三方模块(已经在sys.path中),但是要绝对避免使用import来导入自定义包的子模块(没有在sys.path中),应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。
       
    • 单独导入包:单独导入包名称时不会导入包中所有包含的所有子模块

       
       
       
      x
       
       
       
       
      #在与glance同级的test.py中
      import glance
      glance.cmd.manage.main()
      '''
      执行结果:
      AttributeError: module 'glance' has no attribute 'cmd'
      '''
      #解决办法: 规划路经,使用__init__.py,__all__是用于控制from...import * 
      #glance/__init__.py
      from . import cmd
      #glance/cmd/__init__.py
      from . import manage
      #执行
      #在于glance同级的test.py中
      import glance
      glance.cmd.manage.main()
       

    六、软件开发规范

    img

    七、补充:

     
     
     
    x
     
     
     
     
    # 同一大文件下(项目目录),如何导入各个小文件中的py文件的导入: __file__:当前文件路径
    #项目开始的文件中导入
    import os
    start_path = __file__
    bin_path = os.path.dirname(start_path)  # 翻一层
    project_path = os.path.dirname(bin_path)  # 再翻
    sys.path.append(project_path)  # 添加路径,可以迁移文件
    #规定
    Base_path = os.path.dirname(os.path.dirname(__file__))
    sys.path.append(project_path)  
    #注意的是:在每个模块的开头,需要导入时,都需要使用from 父目录 import ……,也就是说现在项目的所有小目录能找到,所以,我们的子目录都是要记录这些小目录开始 from。
     
  • 相关阅读:
    aiohttp简介及快速使用
    Git的学习与使用
    基于scrapy-redis的分布式爬虫
    异步编程之asyncio简单介绍
    Scrapy框架中的CrawlSpider
    scrapy中selenium的应用
    ua池和代理池
    Scrapy持久化存储
    Scrapy的日志等级和请求传参
    virtualenv搭建Python虚拟环境
  • 原文地址:https://www.cnblogs.com/double-W/p/10658835.html
Copyright © 2011-2022 走看看