Python學習筆記(十七):模塊 和 包

模塊:

python 中的模塊相當於 C/C++ 中的頭文件,在 C/C++ 中需要通過 #include 導入頭文件,纔可以使用頭文件中的函數,而在 python 中導入模塊,要使用 import 關鍵字,導入模塊之後,纔可以使用模塊中的方法;

python 中的模塊是以 .py 爲後綴的文件,它既可以包含函數,也可以包含變量;模塊文件也是源碼文件;

python 中的所有模塊,可以在安裝路徑下的 lib 文件夾下查看,如下所示:

ubuntu 環境下 python3 模塊的位置在 /user/lib/python3.5/:


 

pip:

python 在安裝好之後,就會自帶很多模塊,例如 sys、os、random 等;

但是除了 python 自帶的模塊之外,我們通常還需要使用一些第三方的模塊,這時候就需要我們自己安裝了;

而 pip 就是 python 裏的模塊管理工具,可以用來安裝第三方的模塊;

在 windows 系統下安裝 python 的時候,會自動安裝 pip 工具,可以使用 pip --version 命令查看:

然後使用 pip 安裝 pygame 模塊,直接在終端輸入命令:pip install pygame 即可,如下:

查看安裝好的模塊的詳細信息:

但是在導入 pygame 模塊的時候,總是會輸出 pygame 模塊的版本信息:

解決辦法是到 pygame 安裝的路徑下,找到 __init__.py 文件:

然後打開該文件,找到下面的輸出語句,註釋掉即可:(我的這條輸出語句是倒數第二個語句塊)

 

而在 ubuntu 下安裝模塊時需要注意,因爲 pip 工具是在 python 2.x 下的,所以用 pip 工具安裝模塊時也是將模塊安裝到 python 2.x 中的;查看 pip 版本的時候可以看到如下信息:

如果 ubuntu 系統下默認沒有安裝 pip 工具,可以用 sudo apt install python-pip 命令安裝:

如果你用的是 python 3.x,那麼也可以不用安裝 pip 工具,直接安裝 pip3 工具即可,

查看安裝好之後的 pip3 工具:

安裝好 pip3 工具之後,直接用 sudo pip3 install pygame 命令,就可以將 pygame 模塊安裝到 python 3.x 下了;

但是我用 pip3 工具安裝模塊時,報如下錯誤:

Exception:
Traceback (most recent call last):
  File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/response.py", line 226, in _error_catcher
    yield
  File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/response.py", line 301, in read
    data = self._fp.read(amt)
  ......
  File "/usr/share/python-wheels/urllib3-1.13.1-py2.py3-none-any.whl/urllib3/response.py", line 231, in _error_catcher
    raise ReadTimeoutError(self._pool, None, 'Read timed out.')
requests.packages.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.

這是因爲用國內網絡安裝太慢,導致的超時錯誤,可以使用一些國內的鏡像:

清華大學:        https://pypi.tuna.tsinghua.edu.cn/simple
阿里雲:         http://mirrors.aliyun.com/pypi/simple
中國科技大學:    https://pypi.mirrors.ustc.edu.cn/simple
豆瓣(douban):    http://pypi.douban.com/simple
中國科學技術大學:    http://pypi.mirrors.ustc.edu.cn/simple

臨時使用鏡像可以在 pip3 工具後面加參數 -i,指定源,如下所示:

sudo pip3 install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple

永久使用鏡像:修改 ~/.pip/pip.conf (沒有就創建一個), 內容如下:

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple

但是,我用這種配置文件的方式,還是會報超時異常;

參考資料:http://www.mamicode.com/info-detail-2349450.html

 

pip 模塊管理工具還有一些其他的用法:

  • 安裝已經下載好的安裝包:
pip install 下載好的安裝包
  • 查看已經安裝了哪些模塊:
pip list

  • 顯示已安裝模塊的具體信息:
pip show --files pygame

  • 列出哪些軟件包已過期,即有新的軟件包可以升級:
pip list --outdated

  • 升級軟件包:

windows 系統下要在 pip 工具前面加 python -m,否則會升級失敗:

python -m pip install --upgrade pip

  • 卸載軟件包:
pip uninstall pygame


 

自定義模塊:

一個模塊就是一個 .py 文件,裏面可以包含函數和變量;

比如定義一個模塊叫 MyMath,就是創建一個 MyMath.py 文件;然後在文件中聲明一個變量,和一個函數:

# 變量
dec = "自定義的模塊"

# 函數:求和
def getSum(a, b):
    return a + b

然後在主程序中導入該模塊,並使用模塊中的函數和變量:

# 導入自定義的模塊:import 模塊文件的名字(不用帶 .py 後綴)
import MyMath

print("MyMath.dec:", MyMath.dec)        # 使用模塊中的變量
print("2 + 3 = ", MyMath.getSum(2, 3))  # 使用模塊中的函數

輸出結果:

注意:在使用我們自定義的模塊時,會自動生成一個 __pycache__ 文件夾,然後在該文件夾中自動生成一個 MyMath.cpython-38.pyc 文件;

這個文件是 python 解析器將 MyMath 模塊解析之後生成的字節碼文件,相當於 java 的 .class 文件;cpython 表示 python 解析器是用 c 語言寫的,38 表示 python 的版本是 3.8,.pyc 是字節碼文件的後綴名;


 

導入模塊:

在 python 中可以用 import 或 from ... import ... 來導入模塊;

  • 將整個模塊(somemodule)導入,格式爲:import somemodule
# 導入 MyMath 模塊
import MyMath

# 使用 MyMath 模塊中的變量和函數:
# 如果只導入了模塊名,那麼在使用模塊中的變量和函數時,必須在前面加上模塊名
print("MyMath.dec:", MyMath.dec)        # 使用模塊中的變量
print("MyMath.getSum:", MyMath.getSum(2, 3))  # 使用模塊中的函數
print("getSum:", getSum(2, 3))	# 如果加模塊名會報錯:name 'getSum' is not defined
  • 將某個模塊中的某個函數(somefunction)導入,格式爲:from somemodule import somefunction
# 將 MyMath 模塊中的 getSum 函數導入
from MyMath import getSum

# 因爲已經導入了 getSum 函數,所以可以直接使用
print("getSum:", getSum(2, 3))

# 反而如果在 getSum 函數前面加模塊名,就會報錯:name 'MyMath' is not defined
# print("MyMath.getSum:", MyMath.getSum(2, 3))

# 因爲導入的是 MyMath 模塊中的 getSum 函數,而不是導入了 MyMath 模塊,
# 所以不能使用 MyMath 模塊中的其他功能;
print("MyMath.dec:", MyMath.dec)    # 報錯:name 'MyMath' is not defined
  • 將某個模塊中的多個函數或變量導入,格式爲:from somemodule import firstfunc, secondfunc
# 將 MyMath 模塊中的 dec 變量和 getSum 函數導入
from MyMath import dec, getSum

# 因爲已經導入了 dec 變量和 getSum 函數,所以可以直接使用
print("dec:", dec)
print("getSum:", getSum(2, 3))
  • 將某個模塊中的全部函數和變量導入,格式爲:from somemodule import *
# 將 MyMath 模塊中的全部變量和函數導入
from MyMath import *

# 因爲已經導入了 MyMath 模塊中的所有功能,所以可以直接使用
print("dec:", dec)
print("getSum:", getSum(2, 3))
  • 爲導入的模塊起別名:(如果模塊的名稱比較長,可以使用別名)
# 導入 MyMath 模塊,併爲 MyMath 模塊起個別名 mm
import MyMath as mm

# 使用 mm 代替 MyMath 調用模塊中的變量和函數
print("dec:", mm.dec)
print("getSum:", mm.getSum(2, 3))

注意:如果在不同的模塊中,有名稱相同的函數都被導入了,那麼後一次導入的函數有效;所以,應儘量避免使用 import * 這種方式,因爲存在一定的風險;推薦使用 import somemodule 的方式,可以明確的知道哪個函數出自那個模塊;

導入模塊的時候,先在當前項目路徑下進行搜索,如果搜索不到,再到 python 系統目錄下進行搜索;


 

模塊中的私有屬性:

python 中變量命名的規則:

  • num:前後都沒有下劃線的是公有變量;

  • __num:前置雙下劃線的是私有變量;

  • __init__:前後雙下劃線的是 python 系統內置的變量;

  • _num:前置單下劃線的是私有化的屬性;私有化的屬性在類中的用法跟公有屬性一樣,類對象和子類都可以訪問;但是如果私有屬性用在模塊中,當使用 from somemodule import * 導入模塊時,私有屬性無法使用;

  • num_:單後置下劃線用於避免與 python 關鍵詞的衝突;比如 if_ 可以作爲一個變量;

將自定義模塊 MyMath.py 改爲:

# 變量
dec = "自定義的模塊"

# 模塊中的私有屬性
_num = 100
__num2 = 200

# 函數:求和
def getSum(a, b):
    return a + b

然後在外部使用 from MyMath import * 方式導入模塊:

# 將 MyMath 模塊中的全部變量和函數導入
from MyMath import *

# 因爲已經導入了 MyMath 模塊中的所有功能,所以可以直接使用
print("dec:", dec)
print("getSum:", getSum(2, 3))

# 無法使用模塊中的私有屬性
print("_num:", _num)
print("__num2:", __num)

但是如果在外部使用 import MyMath 方式導入模塊,則可以使用模塊中的私有屬性:

# 導入 MyMath 模塊
import MyMath

# 可以使用模塊中的私有屬性
print("_num:", MyMath._num)
print("__num2:", MyMath.__num2)

 

在模塊中進行測試(__name__ 變量的用法):

python 中在導入一個模塊的時候,會將模塊中的代碼從頭到尾執行一遍;如果我們在模塊中寫了測試程序輸出一些信息,那麼在主程序中導入該模塊的時候,也會執行模塊中的測試代碼輸出信息;

例如,在模塊中寫一個測試語句,如下所示:

# 變量
dec = "自定義的模塊"

# 函數:求和
def getSum(a, b):
    return a + b

print("測試:", getSum(22, 33))

然後在主程序中導入該模塊:

# 導入 MyMath 模塊
import MyMath

# 使用模塊中的函數
print("getSum:", MyMath.getSum(2, 3))

輸出結果:

那麼應該怎麼解決在主程序中導入模塊時,不執行模塊中的測試代碼呢?

此時需要用到一個系統內置的變量 __name__,我們在 MyMath 模塊中輸出該變量的值:

# 變量
dec = "自定義的模塊"

# 函數:求和
def getSum(a, b):
    return a + b

# 輸出 __name__ 變量的值
print("__name__:", __name__)

輸出該變量的值爲:__main__

但是,當我們在外部程序中導入 MyMath 模塊,因爲導入模塊的時候會執行一遍模塊中的所有代碼,此時模塊中輸出 __name__ 變量的值變成了 MyMath 模塊的名字,如下:

所以,如果我們想在模塊中寫一些測試代碼,但是又不想被外部程序導入該模塊的時候,執行這些測試代碼,可以如下操作:

# 變量
dec = "自定義的模塊"

# 函數:求和
def getSum(a, b):
    return a + b

# 測試代碼都寫在該條件語句中,在外部導入該模塊的時候,就不會執行了
if __name__ == "__main__":
    print("測試:", getSum(22, 33))

 

模塊中 __all__ 變量的用法:

我們知道導入模塊中的所有功能可以使用方法:from somemodule import *,而 __all__ 變量的作用就是在使用該方法時限制導入模塊中的某些功能;

自定義一個模塊,名字爲 sendMsg.py,在模塊中使用 __all__ 變量:

# __all__ 變量的作用是限制外部使用 from sendMsg import *
# 方法導入該模塊時,只能導入 __all__ 變量指定的成員;
# __all__ 變量的值是一個字符串列表,列表中的元素不管是變量,
# 還是函數,或是類,都用字符串表示;
# 下面的語句表示 from sendMsg import * 導入該模塊時,
# 只能使用模塊中的變量 num 和 test1 函數,而不能使用 test2 函數;
__all__ = ["num", "test1"]

# 在模塊中聲明一個全局變量
num = 100

# 在模塊中定義兩個函數
def test1():
    print("=== test1 ===")

def test2():
    print("=== test2 ===")

在 main.py 中導入 sendMsg 模塊:

# 導入模塊
from sendMsg import *

# 調用模塊中的變量
print("num:", num)

# 調用模塊中的方法
test1()
test2() # 會調用失敗

輸出結果:


 

包:

一個模塊是指一個 .py 文件,而包是一個包含多個 .py 文件的文件夾,並且該文件夾中必須有一個 __init__.py 文件,有了該文件才能被稱之爲包;即如果文件夾中含有多個 .py 文件,但是沒有 __init__.py 文件,則只是一個普通的文件夾,不能稱之爲包;如下所示;

其中 recvMsg.py 中有一個函數:

# 在 recvMsg 模塊中定義一個函數
def recvTest():
    print("=== recvTest() ===")

而 sendMsg.py 中也有一個函數:

# 在 sendMsg 模塊中定義一個函數
def sendTest():
    print("=== sendTest() ===")

當然,一個普通的文件夾下包含多個模塊,也是可以通過下面方法被外部導入的:

但是,如果文件夾下有很多模塊,我們就需要寫很多個 from ... import ... 語句,那麼能不能通過 import msg 或者 from msg import * 來導入 msg 文件夾下的所有模塊呢?答案是否定的。但是如果 msg 是包,就可以實現。

不過,如果 __init__.py 爲空,也不能直接導入包中的模塊,需要對 __init__.py 進行編輯;

1、在 __init__.py 中使用 __all__ 變量:

前面已經講過 __all__ 變量用在模塊中,用於指定外部使用 from 模塊名 import * 導入該模塊時,可以使用模塊中的哪些功能;

而包中使用 __all__ 變量,用於限制外部使用 from 包名 import * 導入該包時,可以使用包中的哪些模塊;

比如在 __init__.py 中添加如下代碼:

__all__ = ["sendMsg"]

表示外部只能使用包中的 sendMsg 模塊:

# 導入包中的模塊
from msg import *

# 使用包中的模塊
# 因爲 __init__ 中指定 __all__ 變量的值爲 sendMsg,
# 所以可以使用 sendMsg 模塊中的所有功能,
# 而不能使用 recvMsg 模塊中的功能;
sendMsg.sendTest()
recvMsg.recvTest()

輸出結果:

但是,__all__ 變量只能影響 from msg import * 方法,當外部使用 import msg 的時候,還是不能使用包中的模塊;

2、在 __init__.py 中導入包中的模塊:

用於指定當外部使用 import msg 導入包的時候,可以使用包中的哪些模塊;修改 __init__.py 文件爲:

# 導入包中的模塊,下面兩種方法都可以
from . import sendMsg       # 從當前路徑下導入模塊
from msg import recvMsg     # 從 msg 文件夾下導入模塊

在 main.py 中導入 msg 包時,就可以直接使用包中的模塊了:

# 導入包
import msg

# 使用包中的模塊
msg.sendMsg.sendTest()
msg.recvMsg.recvTest()

3、在 __init__.py 中輸出數據:

__all__ = ["sendMsg"]
# 在 __init__.py 中輸出數據
print("=== init ===")

當外部導入包時,會執行 __init__.py 中的所有代碼,所以會直接輸出數據;

前面我們已經講過,當導入模塊時,也會執行模塊中的所有代碼;


 

包的發佈(安裝):

我們知道,當導入一個模塊的時候,首先會在當前項目路徑下搜索該模塊,如果搜不到,再到 Python 系統路徑下搜索;導入包也是一樣的;

而我們自己創建的模塊或包,只能在當前項目路徑下才能使用,如果換個其他的項目,或者路徑,就不能使用了;

如果想不管在什麼路徑下都可以使用我們自定義的模塊或包,那麼就需要把包發佈到 python 的系統路徑下;

發佈包的步驟:

1、在包的同級目錄下創建一個 setup.py 文件,裏面的內容如下:

# 這是固定寫法
from distutils.core import setup

# name:生成的包的名字
# version:包的版本
# description:包的描述信息
# author:作者
# py_modules:用來指定包中的模塊
setup(name="chenhx", version="1.0", description="chenhx's module", author="chenhx", py_modules=['msg.sendMsg', 'msg.recvMsg'])

2、執行 python setup.py build 命令構建包:

3、執行 python setup.py sdist 命令生成發佈壓縮包:

4、安裝:

將 chenhx-1.0.tar.gz 隨便放在哪個路徑下都可以,然後解壓 chenhx-1.0.tar.gz,解壓之後變成:

然後在命令提示符下進入該路徑,執行 python setup.py install 命令進行安裝:

5、到此爲止,已經將包發佈到了 python 的系統路徑下了,不管在什麼位置都可以使用了;我們在 D 盤根目錄下創建一個 main.py 文件,內容爲:

# 導入包
import msg

# 使用包中的模塊
msg.sendMsg.sendTest()
msg.recvMsg.recvTest()

執行結果爲:


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章