模塊與包

1、模塊

  1. 常見的場景:一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的後綴,但其實import加載的模塊分爲四個通用類別:html

    • 使用python編寫的代碼(.py文件)
    • 已被編譯爲共享或DLL的C或C++擴展
    • 包好一組模塊的包
    • 使用C編寫並連接到python解釋器的內置函數
  2. 若是你退出Python解釋器而後再進入,那麼你以前定義的函數或變量都將丟失,所以咱們一般將程序寫到文件中以便永久保存下來,須要時就經過python 文件.py ,此時執行的文件就叫作script。當文件腳本不少時,咱們將程序分紅一個個文件,這樣程序的結構就更清晰,方便管理,這時咱們不只僅能夠把這些文件當成腳本文件,還能夠把他們看成模塊來導入到其餘模塊,實現功能的重用node

  3. 使用模塊: 咱們能夠從sys.modules中找到當前已經加載的模塊python

    • 模塊的導入至關於執行了整個文件web

    • 一個模塊不能被屢次導入,一旦導入原文件中的修改也不會生效,且各個模塊的變量於本模塊不衝突json

       
       
       
      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語句僅是對已經加載大內存中的模塊對象增長了一次引用,不會從新執行模塊內的語句。windows

    • 導入一個模塊的時候命名空間的變化:api

      • 建立了一個要導入模塊的命名空間
      • 建立一個變量指向這個命名空間,其實至關於加載文件類(我的理解)
      • 執行這個文件,注意這個關係是單向引用,也就是說,若是不是函數傳參的形式,是不能把本文件的變量a傳給模塊中的變量a
    •  

    • 模塊雖然一行能夠導入多個,可是不推薦這樣使用。如:import time,os,random,my_moduleapp

    • as語法的使用:less

       
       
       
      xxxxxxxxxx
       
       
       
       
      #一、time這個名字就失效的,只剩下t了
      import time as t
      t.time()
      #二、用來作兼容,當要根據判斷取模塊時,咱們能夠先判斷,再以一樣的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中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就能夠了dom

       
       
       
      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不能被導入
       

       

2、模塊導入的三大問題

  • 模塊的搜索路徑:

    • python解釋器在啓動時會自動加載一些模塊,可使用sys.modules查看,在第一次導入某個模塊時(好比my_module),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),若是有則直接引用.若是沒有,解釋器則會查找同名的內建模塊,若是尚未找到就從sys.path給出的目錄列表中依次尋找my_module.py文件。

       
       
       
      xxxxxxxxxx
       
       
       
       
      #一、因此總結模塊的查找順序是:內存中已經加載的模塊 -> 內置模塊 -> ys.path路徑中包含的模塊
      #二、須要特別注意的是:咱們自定義的模塊名不該該與系統內置模塊重名
      #三、搜索時按照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))
       

       

3、包

  • 包是一種經過使用'模塊名'來組織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文件:
        # 一、如今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文件最好使用絕對導入,這樣,想導入的時候才能找到
       

       

5、絕對導入和相對導入

  • 絕對導入:以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()
     

 

6、軟件開發規範

img

7、補充:

 
 
 
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。
相關文章
相關標籤/搜索