1. 程式人生 > 實用技巧 >14-Python之路---包和模組

14-Python之路---包和模組

模組的概念

模組是 Python 程式架構的一個核心概念

通常模組為一個檔案,直接使用import來匯入就好了。可以作為module的檔案型別有".py"、".pyo"、".pyc"、".pyd"、".so"、".dll"。

每一個以副檔名 py 結尾的 Python 原始碼檔案都是一個 **模組 **

模組名 同樣也是一個 識別符號,需要符合識別符號的命名規則

在模組中定義的 全域性變數函式 都是提供給外界直接使用的 工具

模組 就好比是 工具包,要想使用這個工具包中的工具,就需要先 匯入

這個模組

模組的兩種匯入方式

import 匯入

import 模組名1, 模組名2 

提示:在匯入模組時,每個匯入應該獨佔一行

import 模組名1
import 模組名2
  • 匯入之後
    • 通過 模組名. 使用 模組提供的工具 —— 全域性變數函式

from ... import 匯入

  • 如果希望 從某一個模組 中,匯入 部分 工具,就可以使用 from ... import 的方式
  • import 模組名一次性 把模組中 所有工具全部匯入,並且通過 模組名/別名 訪問
# 從 模組 匯入 某一個工具
from 模組名1 import 工具名
  • 匯入之後
    • 不需要
      通過 模組名.
    • 可以直接使用 模組提供的工具 —— 全域性變數函式

注意

如果 兩個模組,存在 同名的函式,那麼 後匯入模組的函式,會 覆蓋掉先匯入的函式

  • 開發時 import 程式碼應該統一寫在 程式碼的頂部,更容易及時發現衝突
  • 一旦發現衝突,可以使用 as 關鍵字 給其中一個工具起一個別名

使用 as 指定模組的別名

如果模組的名字太長,可以使用 as 指定模組的名稱,以方便在程式碼中的使用

import 模組名1 as 模組別名
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

注意:模組別名 應該符合 大駝峰命名法

變數名重複時使用 as 建立別名

from ... import *(知道)

# 從 模組 匯入 所有工具
from 模組名1 import * # 這種方式不推薦使用,因為函式重名並沒有任何的提示,出現問題不好排查
from requests import get
from requests.api import sessions

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

匯入本地指令碼

import 如果你要匯入的 Python 指令碼與當前指令碼位於同一個目錄下,只需輸入 import,然後是檔名,無需副檔名 .py。

匯入變數

從一個模組中匯入定義好的變數

# filename: a.py

a = 1
# filename: b.py

from a import a
print(a)

匯入方法

從模組中匯入定義好的方法

# filename: a.py

def a():
	print(__name__)
# filename: b.py

from a import a
a()

匯入物件

從模組中匯入定義好的物件

# filename: a.py

class A():
	def b():
		print(__name__)
# filename: b.py

from a import A
a = A()
a.b()

案例:使用 requests 中的 get 方式的三種匯入

# import requests
# from requests import *
# from requests import get
#
# response = requests.get("https://www.baidu.com")
# print(response.status_code)

import 匯入的順序(模組匯入順序)

import sys, os

# sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'xxx')))
print(sys.path)

變數匯入順序

locals()

globals()

使用 if __name__ == '__main__'

為了避免執行從其他指令碼中作為模組匯入的指令碼中的可執行語句,將這些行包含在 if __name__ == '__main__' 塊中。

每當我們執行此類指令碼時,Python 實際上會為所有模組設定一個特殊的內建變數 __name__ 。當我們執行指令碼時,Python 會將此模組識別為主程式,並將此模組的 __name__ 變數設為字串 “main”。對於該指令碼中匯入的任何模組,這個內建 __name__ 變數會設為該模組的名稱。因此,條件 if __name__ == "__main__"會檢查該模組是否為主程式。

匯入模組

  • import xxx
  • from xxx import xxx
  • from xxx import xxx as xx
  • from xxx.xxx import xxx
  • from . import xxx

相對匯入與絕對匯入

絕對匯入的格式為 import A.Bfrom A import B,相對匯入格式為 from .A import Bfrom ..X import Y. 代表當前模組,.. 代表上層模組,... 代表上上層模組,依次類推。

包(Package)

通常包總是一個目錄,可以使用 import 匯入包,或者 from ... import 來匯入包中的部分模組。包目錄下為首的一個檔案便是 __init__.py。然後是一些模組檔案和子目錄,假如子目錄中也有 __init__.py 那麼它就是這個包的子包了。

概念

  • 是一個 包含多個模組特殊目錄
  • 目錄下有一個 特殊的檔案 __init__.py
  • 包名的 命名方式 和變數名一致,小寫字母 + _

好處

  • 使用 import 包名 可以一次性匯入包中所有的模組

普通檔案與包的區別

__init__.py

__all__ == ['a', 'b', 'c']

每一個檔案都應該是可以被匯入的

  • 一個 獨立的 Python 檔案 就是一個 模組
  • 在匯入檔案時,檔案中 所有沒有任何縮排的程式碼 都會被執行一遍!

實際開發場景

  • 在實際開發中,每一個模組都是獨立開發的,大多都有專人負責
  • 開發人員通常會在模組下方增加一些測試程式碼
    • 僅在模組內使用,而被匯入到其他檔案中不需要執行

__name__ 屬性

__name__ 屬性可以做到,測試模組的程式碼 只在測試情況下被執行,而在被匯入時不會被執行

__name__Python 的一個內建屬性,記錄著一個 字串

如果是被其他檔案匯入的,__name__ 就是模組名

如果是當前執行的程式 __name____main__

在很多 Python 檔案中都會看到以下格式的程式碼

# 匯入模組
# 定義全域性變數
# 定義類
# 定義函式

# 在程式碼的最下方
def main():
    # ...
    pass

# 根據 __name__ 判斷是否執行下方程式碼
if __name__ == "__main__":
    main()

pip其他安裝方式

pip 是 Python 包管理工具,該工具提供了對Python 包的查詢、下載、安裝、解除安裝的功能。

目前如果你在 python.org 下載最新版本的安裝包,則是已經自帶了該工具。

常用指令

顯示版本和路徑

pip --version

升級pip

pip install -U pip

指定版本安裝庫

pip install Django==1.7

列出已安裝的包

pip list

注意事項

如果 Python2Python3 同時有 pip,則使用方法如下:

Python2

python2 -m pip install XXX

Python3:

python3 -m pip install XXX

指定下載好的檔案進行安裝

pip install Flask-WTF-0.10.0.tar.gz

pip install requests-master.zip

指定國內源進行安裝

pip install -i https://pypi.douban.com/simple/ requests

pip國內映象源。

阿里雲 http://mirrors.aliyun.com/pypi/simple/
中國科技大學 https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣 http://pypi.douban.com/simple
Python官方 https://pypi.python.org/simple/
v2ex http://pypi.v2ex.com/simple/
中國科學院 http://pypi.mirrors.opencas.cn/simple/

永久更換方法

linux修改 ~/.pip/pip.conf

mkdir ~/.pip 
vim ~/.pip/pip.conf
[global]
index-url = http://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host = mirrors.aliyun.com

Windows下更換pip源為阿里源

1 開啟appdata資料夾,在資源管理器的位址列輸入%appdata%後回車:

2 新建一個pip資料夾,在pip資料夾裡面新建一個配置檔案pip.ini

3 在配置檔案中輸入如下內容後儲存即可:

[global]
timeout = 6000
index-url = http://mirrors.aliyun.com/pypi/simple/
trusted-host = http://mirrors.aliyun.com/pypi/simple/

異常

程式在執行時,如果Python 直譯器遇到 到一個錯誤,會停止程式的執行,並且提示一些錯誤資訊,這就是 異常

程式停止執行並且提示錯誤資訊 這個動作,我們通常稱之為:丟擲(raise)異常

程式開發時,很難將所有的特殊情況都處理的面面俱到,通過異常捕獲可以針對突發事件做集中的處理,從而保證程式的穩定性和健壯性

捕獲異常

在程式開發中,如果 對某些程式碼的執行不能確定是否正確,可以增加 try(嘗試) 來 捕獲異常

捕獲異常最簡單的語法格式:

try:
    嘗試執行的程式碼
except:
    出現錯誤的處理

try 嘗試,下方編寫要嘗試程式碼,不確定是否能夠正常執行的程式碼

except 如果不是,下方編寫嘗試失敗的程式碼

異常——整數

try:
    # 提示使用者輸入一個數字
    num = int(input("請輸入數字:"))
except:
    print("請輸入正確的數字")

錯誤型別捕獲

在程式執行時,可能會遇到不同型別的異常,並且需要針對不同型別的異常,做出不同的響應,這個時候,就需要捕獲錯誤型別了

語法如下:

try:
    # 嘗試執行的程式碼
    pass
except 錯誤型別1:
    # 針對錯誤型別1,對應的程式碼處理
    pass
except (錯誤型別2, 錯誤型別3):
    # 針對錯誤型別2 和 3,對應的程式碼處理
    pass
except Exception as result:
    print("未知錯誤 %s" % result)

Python 直譯器丟擲異常時,最後一行錯誤資訊的第一個單詞,就是錯誤型別

異常捕獲演練

需求

  1. 提示使用者輸入一個整數
  2. 使用 8 除以使用者輸入的整數並且輸出
try:
    num = int(input("請輸入整數:"))
    result = 8 / num
    print(result)
except ValueError:
    print("請輸入正確的整數")
except ZeroDivisionError:
    print("除 0 錯誤")

捕獲未知錯誤

在開發時,要預判到所有可能出現的錯誤,還是有一定難度的

如果希望程式 無論出現任何錯誤,都不會因為 Python 直譯器 丟擲異常而被終止,可以再增加一個 except

語法如下:

except Exception as result:
    print("未知錯誤 %s" % result)

異常捕獲完整語法

在實際開發中,為了能夠處理複雜的異常情況,完整的異常語法如下:

先對這個語法結構有個印象即可

try:
    # 嘗試執行的程式碼
    pass
except 錯誤型別1:
    # 針對錯誤型別1,對應的程式碼處理
    pass
except 錯誤型別2:
    # 針對錯誤型別2,對應的程式碼處理
    pass
except (錯誤型別3, 錯誤型別4):
    # 針對錯誤型別3 和 4,對應的程式碼處理
    pass
except Exception as result:
    # 列印錯誤資訊
    print(result)
else:
    # 沒有異常才會執行的程式碼
    pass
finally:
    # 無論是否有異常,都會執行的程式碼
    print("無論是否有異常,都會執行的程式碼")

else 只有在沒有異常時才會執行的程式碼

finally 無論是否有異常,都會執行的程式碼

之前一個演練的 完整捕獲異常 的程式碼如下:

try:
    num = int(input("請輸入整數:"))
    result = 8 / num
    print(result)
except ValueError:
    print("請輸入正確的整數")
except ZeroDivisionError:
    print("除 0 錯誤")
except Exception as e:
    print("未知錯誤 %s" % e)
else:
    print("正常執行")
finally:
    print("執行完成,但是不保證正確")

異常的傳遞

異常的傳遞 —— 當執行函數出現異常,會將異常傳遞給的呼叫一方

如果傳遞到主程式,仍然沒有異常處理,程式才會被終止

提示

在開發中,可以在主函式中增加異常捕獲,在主函式中呼叫的其他函式,只要出現異常,都會傳遞到主函式的 異常捕獲中。這樣就不需要在程式碼中,增加大量的 異常捕獲,能夠保證程式碼的整潔

需求

  1. 定義函式 demo1() 提示使用者輸入一個整數並且返回
  2. 定義函式 demo2() 呼叫 demo1()
  3. 在主程式中呼叫 demo2()
def demo1():
    return int(input("請輸入一個整數:"))


def demo2():
    return demo1()

try:
    print(demo2())
except ValueError:
    print("請輸入正確的整數")
except Exception as result:
    print("未知錯誤 %s" % result)

丟擲 raise 異常

在開發中,除了 程式碼執行出錯 Python 直譯器會 丟擲 異常之外

還可以根據 應用程式 特有的業務需求 主動丟擲異常

示例

  • 提示使用者 輸入密碼,如果 長度少於 8,丟擲 異常

注意

當前函式 只負責提示使用者輸入密碼,如果 密碼長度不正確,需要其他的函式進行額外處理

因此可以 丟擲異常,由其他需要處理的函式 捕獲異常

丟擲異常

  • Python 中提供了一個 Exception 異常類
  • 在開發時,如果滿足 特定業務需求時,希望 丟擲異常,可以:
    1. 建立 一個 Exception 的 物件
    2. 使用 raise 關鍵字 丟擲 異常物件

需求

  • 定義 input_password 函式,提示使用者輸入密碼
  • 如果使用者輸入長度 < 8,丟擲異常
  • 如果使用者輸入長度 >=8,返回輸入的密碼
def input_password():

    # 1. 提示使用者輸入密碼
    pwd = input("請輸入密碼:")

    # 2. 判斷密碼長度,如果長度 >= 8,返回使用者輸入的密碼
    if len(pwd) >= 8:
        return pwd

    # 3. 密碼長度不夠,需要丟擲異常
    # 1> 建立異常物件 - 使用異常的錯誤資訊字串作為引數
    ex = Exception("密碼長度不夠")

    # 2> 丟擲異常物件
    raise ex


try:
    user_pwd = input_password()
    print(user_pwd)
except Exception as result:
    print("發現錯誤:%s" % result)
def div(num1, num2):
    assert isinstance(num1, int), "值型別不正確"
    assert isinstance(num2, int), "值型別不正確"
    assert num2 != 0, "除數不能為0"
    return num1 / num2


if __name__ == '__main__':
    print(div(100, 0))