1. 程式人生 > >Python(十)模組(使用模組,安裝模組,作用域)

Python(十)模組(使用模組,安裝模組,作用域)

模組定義

在計算機程式的開發過程中,隨著程式程式碼越寫越多,在一個檔案裡程式碼就會越來越長,越來越不容易維護。

為了編寫可維護的程式碼,我們把很多函式分組,分別放到不同的檔案裡,這樣,每個檔案包含的程式碼就相對較少,很多程式語言都採用這種組織程式碼的方式。在Python中,一個.py檔案就稱之為一個模組(Module)。

使用模組有什麼好處?

最大的好處是大大提高了程式碼的可維護性。其次,編寫程式碼不必從零開始。當一個模組編寫完畢,就可以被其他地方引用。我們在編寫程式的時候,也經常引用其他模組,包括Python內建的模組和來自第三方的模組。

使用模組還可以避免函式名和變數名衝突。相同名字的函式和變數完全可以分別存在不同的模組中,因此,我們自己在編寫模組時,不必考慮名字會與其他模組衝突。但是也要注意,儘量不要與內建函式名字衝突。點

這裡檢視Python的所有內建函式。

你也許還想到,如果不同的人編寫的模組名相同怎麼辦?為了避免模組名衝突,Python又引入了按目錄來組織模組的方法,稱為包(Package)。

舉個例子,一個abc.py的檔案就是一個名字叫abc的模組,一個xyz.py的檔案就是一個名字叫xyz的模組。

現在,假設我們的abcxyz這兩個模組名字與其他模組衝突了,於是我們可以通過包來組織模組,避免衝突。方法是選擇一個頂層包名,比如mycompany,按照如下目錄存放:

mycompany
├─ __init__.py
├─ abc.py
└─ xyz.py

引入了包以後,只要頂層的包名不與別人衝突,那所有模組都不會與別人衝突。現在,abc.py

模組的名字就變成了mycompany.abc,類似的,xyz.py的模組名變成了mycompany.xyz

請注意,每一個包目錄下面都會有一個__init__.py的檔案,這個檔案是必須存在的,否則,Python就把這個目錄當成普通目錄,而不是一個包。__init__.py可以是空檔案,也可以有Python程式碼,因為__init__.py本身就是一個模組,而它的模組名就是mycompany

類似的,可以有多級目錄,組成多級層次的包結構。比如如下的目錄結構:

mycompany
 ├─ web
 │  ├─ __init__.py
 │  ├─ utils.py
 │  └─ www.py
 ├─ __init__.py
 ├─ abc.py
 └─ xyz.py

檔案www.py的模組名就是mycompany.web.www,兩個檔案utils.py的模組名分別是mycompany.utilsmycompany.web.utils

 自己建立模組時要注意命名,不能和Python自帶的模組名稱衝突。例如,系統自帶了sys模組,自己的模組就不可命名為sys.py,否則將無法匯入系統自帶的sys模組。

mycompany.web也是一個模組,請指出該模組對應的.py檔案。

總結

模組是一組Python程式碼的集合,可以使用其他模組,也可以被其他模組使用。

建立自己的模組時,要注意:

  • 模組名要遵循Python變數命名規範,不要使用中文、特殊字元;
  • 模組名不要和系統模組名衝突,最好先檢視系統是否已存在該模組,檢查方法是在Python互動環境執行import abc,若成功則說明系統存在此模組。

使用模組

Python本身就內建了很多非常有用的模組,只要安裝完畢,這些模組就可以立刻使用。

我們以內建的sys模組為例,編寫一個hello的模組:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a test module '

__author__ = 'Michael Liao'

import sys

def test():
    args = sys.argv
    if len(args)==1:
        print('Hello, world!')
    elif len(args)==2:
        print('Hello, %s!' % args[1])
    else:
        print('Too many arguments!')

if __name__=='__main__':
    test()

第1行和第2行是標準註釋,第1行註釋可以讓這個hello.py檔案直接在Unix/Linux/Mac上執行,第2行註釋表示.py檔案本身使用標準UTF-8編碼;

第4行是一個字串,表示模組的文件註釋,任何模組程式碼的第一個字串都被視為模組的文件註釋;

第6行使用__author__變數把作者寫進去,這樣當你公開原始碼後別人就可以瞻仰你的大名;

以上就是Python模組的標準檔案模板,當然也可以全部刪掉不寫,但是,按標準辦事肯定沒錯。

後面開始就是真正的程式碼部分。

你可能注意到了,使用sys模組的第一步,就是匯入該模組:

import sys

匯入sys模組後,我們就有了變數sys指向該模組,利用sys這個變數,就可以訪問sys模組的所有功能。

sys模組有一個argv變數,用list儲存了命令列的所有引數。argv至少有一個元素,因為第一個引數永遠是該.py檔案的名稱,例如:

執行python3 hello.py獲得的sys.argv就是['hello.py']

執行python3 hello.py Michael獲得的sys.argv就是['hello.py', 'Michael]

最後,注意到這兩行程式碼:

if __name__=='__main__':
    test()

當我們在命令列執行hello模組檔案時,Python直譯器把一個特殊變數__name__置為__main__,而如果在其他地方匯入該hello模組時,if判斷將失敗,因此,這種if測試可以讓一個模組通過命令列執行時執行一些額外的程式碼,最常見的就是執行測試。

我們可以用命令列執行hello.py看看效果:

$ python3 hello.py
Hello, world!
$ python hello.py Michael
Hello, Michael!

如果啟動Python互動環境,再匯入hello模組:

$ python3
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>>

匯入時,沒有列印Hello, word!,因為沒有執行test()函式。

呼叫hello.test()時,才能打印出Hello, word!

>>> hello.test()
Hello, world!

作用域

在一個模組中,我們可能會定義很多函式和變數,但有的函式和變數我們希望給別人使用,有的函式和變數我們希望僅僅在模組內部使用。在Python中,是通過_字首來實現的。

正常的函式和變數名是公開的(public),可以被直接引用,比如:abcx123PI等;

類似__xxx__這樣的變數是特殊變數,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊變數,hello模組定義的文件註釋也可以用特殊變數__doc__訪問,我們自己的變數一般不要用這種變數名;

類似_xxx__xxx這樣的函式或變數就是非公開的(private),不應該被直接引用,比如_abc__abc等;

之所以我們說,private函式和變數“不應該”被直接引用,而不是“不能”被直接引用,是因為Python並沒有一種方法可以完全限制訪問private函式或變數,但是,從程式設計習慣上不應該引用private函式或變數。

private函式或變數不應該被別人引用,那它們有什麼用呢?請看例子:

def _private_1(name):
    return 'Hello, %s' % name

def _private_2(name):
    return 'Hi, %s' % name

def greeting(name):
    if len(name) > 3:
        return _private_1(name)
    else:
        return _private_2(name)

我們在模組裡公開greeting()函式,而把內部邏輯用private函式隱藏起來了,這樣,呼叫greeting()函式不用關心內部的private函式細節,這也是一種非常有用的程式碼封裝和抽象的方法,即:

外部不需要引用的函式全部定義成private,只有外部需要引用的函式才定義為public。

 

安裝第三方模組

在Python中,安裝第三方模組,是通過包管理工具pip完成的。

如果你正在使用Mac或Linux,安裝pip本身這個步驟就可以跳過了。

如果你正在使用Windows,請參考安裝Python一節的內容,確保安裝時勾選了pipAdd python.exe to Path

在命令提示符視窗下嘗試執行pip,如果Windows提示未找到命令,可以重新執行安裝程式新增pip

注意:Mac或Linux上有可能並存Python 3.x和Python 2.x,因此對應的pip命令是pip3

例如,我們要安裝一個第三方庫——Python Imaging Library,這是Python下非常強大的處理影象的工具庫。不過,PIL目前只支援到Python 2.7,並且有年頭沒有更新了,因此,基於PIL的Pillow專案開發非常活躍,並且支援最新的Python 3。

一般來說,第三方庫都會在Python官方的pypi.python.org網站註冊,要安裝一個第三方庫,必須先知道該庫的名稱,可以在官網或者pypi上搜索,比如Pillow的名稱叫Pillow,因此,安裝Pillow的命令就是:

pip install Pillow

耐心等待下載並安裝後,就可以使用Pillow了。

且慢

安裝常用模組

在使用Python時,我們經常需要用到很多第三方庫,例如,上面提到的Pillow,以及MySQL驅動程式,Web框架Flask,科學計算Numpy等。用pip一個一個安裝費時費力,還需要考慮相容性。我們推薦直接使用Anaconda,這是一個基於Python的資料處理和科學計算平臺,它已經內建了許多非常有用的第三方庫,我們裝上Anaconda,就相當於把數十個第三方模組自動安裝好了,非常簡單易用。

可以從Anaconda官網下載GUI安裝包,安裝包有500~600M,所以需要耐心等待下載。網速慢的同學請移步國內映象。下載後直接安裝,Anaconda會把系統Path中的python指向自己自帶的Python,並且,Anaconda安裝的第三方模組會安裝在Anaconda自己的路徑下,不影響系統已安裝的Python目錄。

安裝好Anaconda後,重新開啟命令列視窗,輸入python,可以看到Anaconda的資訊:

┌────────────────────────────────────────────────────────┐
│Command Prompt - python                           - □ x │
├────────────────────────────────────────────────────────┤
│Microsoft Windows [Version 10.0.0]                      │
│(c) 2015 Microsoft Corporation. All rights reserved.    │
│                                                        │
│C:\> python                                             │
│Python 3.6.3 |Anaconda, Inc.| ... on win32              │
│Type "help", ... for more information.                  │
│>>> import numpy                                        │
│>>> _                                                   │
│                                                        │
│                                                        │
│                                                        │
└────────────────────────────────────────────────────────┘

可以嘗試直接import numpy等已安裝的第三方模組。

模組搜尋路徑

當我們試圖載入一個模組時,Python會在指定的路徑下搜尋對應的.py檔案,如果找不到,就會報錯:

>>> import mymodule
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named mymodule

預設情況下,Python直譯器會搜尋當前目錄、所有已安裝的內建模組和第三方模組,搜尋路徑存放在sys模組的path變數中:

>>> import sys
>>> sys.path
['', '/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/site-packages']

如果我們要新增自己的搜尋目錄,有兩種方法:

一是直接修改sys.path,新增要搜尋的目錄:

>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')

這種方法是在執行時修改,執行結束後失效。

第二種方法是設定環境變數PYTHONPATH,該環境變數的內容會被自動新增到模組搜尋路徑中。設定方式與設定Path環境變數類似。注意只需要新增你自己的搜尋路徑,Python自己本身的搜尋路徑不受影響。