python模塊與包的調用

模塊(module)

什麼是模塊

一個 .py文件 就是一個模塊(Module)。
在開發過程當中咱們不會把全部的代碼都寫在一個 .py文件 中。隨着代碼量的增大,能夠按照功能將函數或者類分開存放到不一樣的 .py文件 中。
 
這樣代碼更方便管理,以及後期的維護,也便於其餘程序來調用當前已經實現的功能~
 
在開發過程當中,咱們也常常引用其餘模塊,例如:time,os,configparser,re 等等
在Python中模塊通常有以下3種:
1)Python內置模塊
2)第三方模塊
3)自定義模塊python

模塊的導入

import 語句

導入模塊的語句以下:app

import module1[, module2[,... moduleN]
或
import module1
import module2
...
import moduleN

具體使用哪種方式根據我的習慣而定,導入模塊後,模塊中的方法或者類能夠經過 模塊名.方法() 直接調用~ide

>>> import time
>>> time.time()                     # time() 爲 time模塊中的方法
1545832129.4365451
>>> import datetime
>>> datetime.datetime.now()      # datetime 爲datetime模塊中的類
datetime.datetime(2018, 12, 26, 21, 49, 2, 805953)

當咱們使用 import 語句導入模塊時,Python解釋器首先會去內置名稱空間中尋找,即判斷導入的模塊是否是內置模塊(例如time模塊就是Python內置模塊),而後再去 sys.path 列表中定義的路徑從前日後尋找 .py文件
以下是在我的筆記本上輸出的 sys.path列表:函數

# pycharm中進行輸出:
['/Users/baby/PycharmProjects/untitled/module',
'/Users/baby/PycharmProjects/untitled',
'/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '/usr/local/lib/python3.7/site-packages',
'/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend']

# 在終端進行輸出:
>>> sys.path
['', '/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload',
'/usr/local/lib/python3.7/site-packages']
  • 能夠看到 sys.path 在pycharm中的輸出和在終端的輸出略有區別,在pycharm中,pycharm會自動將當前項目的路徑添加在 sys.path 列表的最前面。因此如果在當前路徑下存在與要引入模塊同名的文件,就會把要引入的模塊屏蔽掉~
     
    sys.path 列表中其中一個路徑下的文件 以下:
    ➜  ~ ls /usr/local/Cellar/python/3.7.1/Frameworks/Python.framework/Versions/3.7/lib/python3.7
    LICENSE.txt                             fileinput.py                            re.py
    __future__.py                           fnmatch.py                              reprlib.py
    __phello__.foo.py                       formatter.py                            rlcompleter.py
    ...
    ...

import 的過程

如今我本身編輯了一個模塊 sftp,內容以下:spa

server_ip = '192.168.0.30'

def get_file():
    print('ddownload file ...')

而後在 main.py文件(與sftp.py在同一個路徑下) 中進行導入:調試

import sftp

 
在 import sftp 時,Python解釋器會首先建立一個新的名稱空間,這個名稱空間用於存放 sftp 模塊中定義的名字,而後在該名稱空間中執行 sftp.py 文件。
例如如今在 sftp 模塊中添加 print 語句,而後執行 main.py文件:code

# sftp.py
server_ip = '192.168.0.30'

def get_file():
    print('ddownload file ...')

print('hello ....')

# main.py
import sftp

# 執行 main.py 後會有以下輸出:
hello ....

import語句 能夠理解爲定義了一個變量,而該變量就指向對應的名稱空間,經過使用這個變量來引用該名稱空間中的方法及變量~
 
import sftp 以後,注意區分新建立的名稱空間和當前的名稱空間,示例以下:orm

# sftp.py
server_ip = '192.168.0.30'

def get_file():
    print('ddownload file ...')

# main.py
import sftp

server_ip = '1.2.3.4'

print(server_ip)
print(sftp.server_ip)

# 執行 main.py 後會有以下輸出:
1.2.3.4
192.168.0.30
  • 注意,sftp.py 中的 server_ip 和 main.py文件中的 server_ip 位於不一樣的名稱空間下,因此名稱相同不會衝突;固然調用的方式也不一樣,當前文件中的 server_ip 能夠直接引用,sftp模塊中的 server_ip 須要使用 模塊名.變量名 的方式來引用(sftp.server_ip)
     
    還有一點須要注意,如果一個模塊在當前文件中被導入屢次,那麼模塊中的代碼只會被執行一次,而不會屢次執行~。那是由於第一次導入,模塊中的方法和變量已經加載到內存中,以後的導入不會重複加載~

重命名模塊

在導入模塊的時候還能夠對模塊進行重命名,以方便使用;如果當前文件中存在同名的方法或變量,也能夠經過這種方式避免衝突~server

import datetime as date
date.datetime.now()

from import

from import 語法以下:blog

from modname import name1[, name2[, ... nameN]]

import語句的導入會新建一個名稱空間,將模塊中的名稱存放在該名稱空間中,而 'from modname import name1, name2' 則會將name1 和 name2 單個導入到當前的名稱空間中。既然是導入到當前的名稱空間中,那就能夠直接拿來使用,前面不須要再添加模塊名稱。

from datetime import datetime
print(datetime.now())    # 不須要寫成 datetime.datetime.now()

 
如果 from … import 致使了名稱重讀,則哪個後定義,就使用哪個

def foo():
    pass
from demo import foo

# 這裏引用 foo 函數,會使用 demo 模塊中的 foo函數

#############
from demo import foo
def foo():
    pass

# 這裏引用 foo 函數,會使用 當前文件中的 foo函數

包(package)

簡單而言,Python中的包(Package)就是一個目錄,裏面存放了 .py文件,外加一個 __init__.py。經過目錄的方式來組織衆多的模塊,包就是用來管理和分類模塊的。引入包以後,還有一個好處就是 同名的模塊能夠放在不一樣的包下,以免名稱衝突~
 
例如如今有以下3個包,ROOT,pk_1,pk_2:
python模塊與包的調用
模塊m1的全名是:ROOT.pk_1.m1;模塊m2的全名則是:ROOT.pk_2.m2 ~

init.py 文件的做用

在每個包目錄下,都應該有一個 __init__.py 文件,若這個文件不存在,那麼這個目錄只是一個目錄而不是一個包。__init__.py 文件能夠是空文件,也能夠有 Python 代碼,原則是儘可能保持 __init__.py 文件的精簡~
 
導入包的語句以下:

import package 

# 或引入包下的某一個模塊
from package import module

import package 或者 from package import module 都會執行package 包下的 __init__ 文件
 

如今有以下目錄結構:

├─ROOT                               
│  ├─pk_1
│  │  ├─__init__.py 
│  │  ├─m1.py    
│  ├─pk_2  
│  │  ├─__init__.py 
│  │  └─m2.py
│  ├─__init__.py
│  ├─test.py

pk_1 和 pk_2 包中的 __init__.py 文件都爲空,ROOT包下的 test.py 想要使用 pk_1 包下 m1模塊中的方法,可使用以下語句:

from pk_1 import m1
m1.fun_1()        # fun_1() 爲m1模塊中的方法

可是使用以下語句,就會拋出異常:

from pk_1 import *
m1.fun_1()

# 異常信息:
NameError: name 'm1' is not defined
##############################
import pk_1
pk_1.m1.fun_1()

# 異常信息:
AttributeError: module 'pk_1' has no attribute 'm1'

這時候能夠在 pk_1 包中的__init__.py 中 進行 包提高(在包中提高導入權限),pk_1 包的 __init__.py 文件內容以下:

from pk_1.m1 import fun_1

而後在 test.py 文件中能夠直接經過包名引入方法:

# 1)
from pk_1 import fun_1 # 或 from pk_1 import *
fun_1()

# 2)
import pk_1
pk_1.fun_1()

 
這個就是 包中 __init__.py 文件存在的意義,能夠將相關的導入語句 或 提高導入權限的語句 寫在 __init__.py文件中,這樣使用者就不須要了解包中的內部結構,能夠直接經過包名 調用該包(package)中某個模塊的方法~
 
還能夠在 包中 __init__.py 文件中使用 __all__ 列出須要導入的模塊,例如在 pk_1 包中的 __init__.py文件中添加 __all__ 變量:

__all__ = ['m2']

而後在 test.py 文件中就可使用 from pk_2 import * 一次性導入 __all__變量中列出的模塊:

from pk_2 import *
m2.fun_2()

如果 pk_2 包的 __init__.py 文件已經對 fun_2 方法作了提高:

# pk_2 包的 \_\_init\_\_.py 內容
from pk_2.m2 import fun_2

這樣在 test.py 中 import * 後可直接使用該方法:

from pk_2 import *
fun_2()

注意:當 __init__.py 中定義了 __all__ 變量時,import * 只會導入 __all__中列出的模塊

包中的模塊調用

如今有以下目錄結構:

├─log                           
│  ├─util
│  │  ├─__init__.py 
│  │  ├─a.py    
│  │  ├─b.py    
│  ├─__init__.py
│  ├─test.py

在test中引入 a模塊:

from util import a

在 a 模塊中又引入了 b 模塊:

import b

這樣的話在執行 test 文件時就會報錯,ModuleNotFoundError: No module named 'b',說沒法找到b模塊。
 
這是由於 在執行 test 時,sys.path 中的路徑不包含 util 下的路徑,因此在 a.py 文件中 import b 模塊時就會報錯(如果直接執行的是 a.py 文件就不會有問題)。在 a模塊 中引入 b 模塊的正確的寫法是:

from util import b

固然這個時候 a.py 文件就不能再單獨運行了,運行時就會報錯
 
Tip:在 a.py 文件中 使用 "from util import b" 導入模塊 b,這個時候如果直接執行 a.py 文件就會報錯,由於 a.py 文件自己就位於util路徑下,sys.path(執行 a.py 時的sys.path)中有util路徑,可是 'from util' 是找不到util的,util 位於 sys.path 的某個路徑下時,'from util' 才能找到util ~
 
如果如今 主執行文件 自己就位於項目目錄下的某個包中,要引入其餘包中的模塊,就須要經過在 os.path 中添加路徑來實現:
python模塊與包的調用
 
如今執行文件是 bin 目錄下的 bin.py,在 bin.py 中要導入 util包 中的 a模塊 和 b模塊,爲了保證通用性,可使用以下方式獲取 log 路徑,而且添加到os.path中:

import sys, os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from util import a

__name__變量

__name__ 與 __file__同樣,是一個內置變量,這個變量記錄了當前文件(使用 __name__ 變量的文件)是做爲模塊運行仍是主執行文件~
示例:
a.py 文件內容

print(__name__)

# 直接執行 a.py 文件,輸出:
# __main__

 
如今 在b.py文件中 import a。b.py 文件內容以下:

import a

# 如今運行 b.py 文件(這個過程會運行 a.py 文件),輸出內容:
# a       ## 即 a 的模塊名稱

這個功能常常被用於代碼的調試:

if __name__=='__main__':
    pass

能夠將調試的代碼寫在 if 語句中,用於調試當前py文件中的代碼,由於直接運行當前文件, __name__ 變量的值就是 __main__。當外部模塊調用的時候,就不會執行 if 語句中的內容,由於 外部模塊調用 __name__ 變量的值 爲模塊名稱~.................^_^

相關文章
相關標籤/搜索