模塊:
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()
執行結果爲: