1. 程式人生 > >【python小課堂專欄】python小課堂15 - 史上最詳細的包和模組import講解篇

【python小課堂專欄】python小課堂15 - 史上最詳細的包和模組import講解篇

python小課堂15 - 史上最詳細的包和模組import講解篇

前言

在大量的程式碼設計中,我們不可能將所有程式碼都寫在一個.py檔案,所以有了包、模組,而為了程式碼可以重複利用(複用性),就有了類、函式的概念。類和函式在下次介紹。

python中的包

python中的包,對應到計算機中,可以理解為資料夾,但是檔案加下必須有一個名為__init__.py的檔案,若沒有此檔案,python則會認為其只是一個普通的資料夾。

開啟pycharm,建立一個包,如下:
在這裡插入圖片描述

在這裡插入圖片描述

python中的模組

python中的模組就非常好理解了,實際上,之前所有的.py檔案,我們都可以稱之為一個模組。單獨的一個py檔案就是一個模組。

在這裡插入圖片描述

在這裡插入圖片描述

test1和test2不同區別就是test2是和package這個包是同級目錄,而test1是屬於package包的。

再來看下總的概念:

在這裡插入圖片描述

包和模組的引入

1.模組處於同級目錄並且不在包下

當我們想在一個模組中使用另一個模組中的變數時,如何操作呢?test2、test3處於同一級目錄。
在這裡插入圖片描述

我想在test3中引入test2的變數,test2.py中有個變數a = 2。
在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

如上所示,只需要在當前模組,用import語句,即可匯入模組,具體使用的時候需要用模組的名字.變數。

import 後面必須是模組的名稱! ------> import modul name

還有一種寫法如下圖pycharm中:
在這裡插入圖片描述

如上所示,只需要在當前模組, from 模組名字 import 變數

2.模組處於同級目錄在同一包下

來看下,test1,test4都屬於package包下的模組。

在這裡插入圖片描述

test1.py中有著字串a = ‘I am success!’
在這裡插入圖片描述

在test4.py中引用test1.py中的a,如何引用呢?

可以看到如下:
在這裡插入圖片描述

關鍵語法:import 包名.模組名 as 別名

但是!!!!!!!如果我們脫離pycharm,找到本機相應的python目錄,通過cmd來執行下,看下效果如何:

在這裡插入圖片描述

在這裡插入圖片描述

可以清晰的看到上圖,通過命令列模式執行就會報錯!錯誤顯示模組沒有被找到:沒有模組叫’package’。這是為什麼呢?在pycharm中通過右鍵run as執行test4,可以看到控制檯成功輸出,而本地呼叫命令列的形式就報錯了!

開啟pycharm的setting,搜尋 python console,右側其中有一項,add content roots to pythonpath,預設pycharm是勾選上此項的。此項的意思是將內容的根路徑加到python的環境變數路徑下。
在這裡插入圖片描述

可以看到上圖下面程式碼塊裡寫著一堆程式碼,正是這段程式碼,我們才可以在pycharm中正確執行。

我們可以在test1.py裡來看下sys.path,順便列印看下結果。
在這裡插入圖片描述

pycharm控制檯輸出:

['F:\\pycharm\\python14\\package', 'F:\\pycharm\\python14', 
'D:\\python3.6\\python36.zip', 
'D:\\python3.6\\DLLs', 'D:\\python3.6\\lib', 'D:\\python3.6', 
'C:\\Users\\sy\\AppData\\Roaming\\Python\\Python36\\site-packages', 
'D:\\python3.6\\lib\\site-packages', 
'D:\\python3.6\\lib\\site-packages\\win32', 
'D:\\python3.6\\lib\\site-packages\\win32\\lib',
'D:\\python3.6\\lib\\site-packages\\Pythonwin']

實際通過命令列輸出,應該沒有’F:\pycharm\python14’ 這一項,因為這一項是pycharm中setting自動加上的!

實際控制檯輸出:

['F:\\pycharm\\python14\\package',
'D:\\python3.6\\python36.zip', 
'D:\\python3.6\\DLLs', 'D:\\python3.6\\lib', 'D:\\python3.6', 
'C:\\Users\\sy\\AppData\\Roaming\\Python\\Python36\\site-packages', 
'D:\\python3.6\\lib\\site-packages', 
'D:\\python3.6\\lib\\site-packages\\win32', 
'D:\\python3.6\\lib\\site-packages\\win32\\lib',
'D:\\python3.6\\lib\\site-packages\\Pythonwin']

sys.path是一個list。默然情況下python匯入檔案或者模組的話,他會先在sys.path裡找模組的路徑。如果沒有的話,程式就會報錯。可以看到,sys路徑下有package的包名,而沒有test4.py中引用test1.py模組。

而pycharm能夠成功執行,正是因為它已經幫我們把專案的根路徑新增到了python的環境變數中。所以我們仿照其類似寫法也可以完成!

解決方案:

這裡不得不說幾個重要的python自帶模組了,如下:

__file__ : python模組自身的名稱
pycharm列印下__file__:

可以看到pycharm會將模組的絕對路徑輸出到控制檯上。

在這裡插入圖片描述

在用命令列執行下看看:

在這裡插入圖片描述

python額外小知識:可以看到上圖有一個__pycache__的資料夾,這個資料夾在pycharm的目錄中,我們是看不到的,那麼此資料夾的意義何在呢?點進去看下:

在這裡插入圖片描述

Python程式執行時不需要編譯成二進位制程式碼,而直接從原始碼執行程式,簡單來說是,Python直譯器將原始碼轉換為位元組碼,然後再由直譯器來執行這些位元組碼。而直譯器的具體工作:
1、完成模組的載入和連結。
2、將原始碼編譯為PyCodeObject物件(即位元組碼),寫入記憶體中,供CPU讀取。
3、從記憶體中讀取並執行,結束後將PyCodeObject寫回硬碟當中,也就是複製到.pyc或.pyo檔案中,以儲存當前目錄下所有指令碼的位元組碼檔案。
4、若再次執行該指令碼,它先檢查【本地是否有上述位元組碼檔案】和【該位元組碼檔案的修改時間是否在其原始檔之後】,是就直接執行,否則重複上述步驟。
第一次執行程式碼的時候,Python直譯器已經把編譯的位元組碼放在__pycache__資料夾中,這樣以後再次執行的話,如果被呼叫的模組未發生改變,那就直接跳過編譯這一步,直接去__pycache__資料夾中去執行相關的 *.pyc 檔案,大大縮短了專案執行前的準備時間。
CSDN來源:
https://blog.csdn.net/index20001/article/details/73501375

繼續迴歸正題:

import sys,os   :  sys ,os模組是python系統自帶模組

os模組: operate system 作業系統的意思,一般可以通過呼叫此模組來對系統進行相關操作

sys 模組: system 系統的意思,通過此模組來實現對python自定義包和模組的匯入

有了以上兩個知識點,我們可以對test4.py進行如下操作:

import sys,os

print(__file__)
print(os.path.abspath(__file__))
print(os.path.dirname(os.path.abspath(__file__)))
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

因為pycharm會對__file__進行路徑補充,所以我們用命令列來執行test4.py

在這裡插入圖片描述

可以看到上圖結果:

__file__             模組名字
test4.py           
os.path.abspath(__file__)     模組名字的絕對路徑
F:\pycharm\python14\package\test4.py
os.path.dirname(os.path.abspath(__file__))    模組的包名絕對路徑
F:\pycharm\python14\package

os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
F:\pycharm\python14                           專案本身的絕對路徑

通過最後一步,我們可以將專案本身的路徑直接拼入python的sys下

base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_path)

驗證究竟有沒有加到我們的python環境變數中,最終程式碼為:

import sys, os

print(sys.path)
print(__file__)
print(os.path.abspath(__file__))
print(os.path.dirname(os.path.abspath(__file__)))
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_path)
print(sys.path)

通過命令列執行來看下:
在這裡插入圖片描述

有了以上的所有操作步驟,我們可以完美的將test1.py的a變數引入test4.py中了!來看下命令執行:


import sys, os
base_path = os.path.dirname(os.path.dirname(
                            os.path.abspath(__file__)))
sys.path.append(base_path)
----------------  sys拼接 一定要在自定義包引入之前定義   ----------------------------------
import package.test1 as test1       注意import的順序。
print(test1.a)

在這裡插入圖片描述

成功!

寫到這裡涉及的知識點就已經這麼多了。。。繼續寫。。。

3.包處於同級目錄包和包同級,包1下的模組引入包2下的模組變數

在這裡插入圖片描述

可以看到,通過from test3 import c,pycharm中是正常輸出的,控制檯是報錯的!原因實際和“2.模組處於同級目錄(在同一包下)”的解釋是一樣的,只需要在引入自定義包之前,將我們專案的根路徑加到python的系統變數中即可。

4.模組處於不同級目錄包和模組同級,模組引入包下模組的變數
在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

若屬於3的情況,可以看到,不需要對python系統進行sys.append,可以正常使用import 或者 from 語句進行匯入。

5.模組處於不同級目錄包和模組同級,包下模組引入與包模組同級的變數

test3.py 中有:
c = 123455666
在packeage下的test1.py呼叫:
在這裡插入圖片描述

可以看到,通過from test3 import c,pycharm中是正常輸出的,控制檯是報錯的!原因實際和“2.模組處於同級目錄(在同一包下)”的解釋是一樣的,只需要在引入自定義包之前,將我們專案的根路徑加到python的系統變數中即可。

import sys, os
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_path)
from test3 import c
print(c)

在這裡插入圖片描述

模組之前的相對引入

什麼叫相對引入呢?相對路徑大家可能聽說過,相對引入和相對路徑是一個道理的,比如 .代表的是當前目錄,…代表的是上級目錄,此處的寫法就是相對路徑,相對於某個檔案來說,即相對!

實際上,在2.模組處於同級目錄(在同一包下)中,還有一種相對引入的寫法,但是對於test4.py引入test1.py來說,不能直接執行test4.py,否則會報錯。來看下:
在這裡插入圖片描述

test1.py中有個a字串:

test1.py
a = 'I am success !'

test4.py中,我用from .test1 import a來引入test1.py的變數a,注意,包下同級目錄,我使用的是.test1 !!!!

test4.py

from .test1 import a
b = a + 'I am test4.py import .test1'

如果此時我直接將test4.py執行,並且列印b,就會報錯!

在這裡插入圖片描述

ModuleNotFoundError: No module named '__main__.test1'; '__main__' is not 
a package

如果此時,我通過test2.py間接行呼叫test4.py中的b

from package.test4 import b
print(b)

無論是pycharm還是命令列,都是有成功執行的:

在這裡插入圖片描述

也就是說python對於相對引入來說,主動引入的函式不能作為主體去執行!

pycharm中可能會遇到的import報錯

有人可能會遇到,當一個新專案匯入到pycharm中,python程式碼的import有可能會報錯,可以將專案設定為根路徑,這樣import錯誤即可消失,操作如下:
在這裡插入圖片描述

包和模組自身的額外小知識點

  1. 關於包下的 init.py

init,中文意思是初始化的意思,而__init__.py實際上就是作為包名來配合的,當我們呼叫一個包時,第一步python就會去呼叫__init__.py模組,所以,經常我們可以將包下的__init__.py中放入一些需要初始化的操作。

舉個例子:
在這裡插入圖片描述

__init__.py:
init_a = 'I am __init__.py'
print(init_a)

在package包下定義了初始化的字串。
而test2.py呼叫package下的test1.py中的a變數時:

test2.py:
from package.test1 import a
print(a)
test1.py:
a = 'I am success !'

可以看到下圖執行結果,先輸出了初始化模組中的字串:

在這裡插入圖片描述

  1. 關於模組中的限定變數寫法

依然是test2.py引入test1.py的變數:
在這裡插入圖片描述


test2.py:
from package.test1 import a,b,c
print(a)
print(b)
print(c)
test1.py:
__all__ = ['a','b']
a = 'I am success !'
b = 'I am fail !'
c = 'I am fuc***  you!!! !'

在test2中引入test1通過import單獨引入三個變數,執行結果:

在這裡插入圖片描述

若將import 後面改成* ,則會限制變數。

在這裡插入圖片描述

而此處所說,就是因為在test1.py中有著__all__ = [] ,這樣的寫法可以限定住import * 的限制,test4.py import *時,則會被限制住罵人的語句!

import 模組的萬金油方法

上面說了這麼多種情況,如果你實在是記不住,那麼請記住一點,萬金油的import方式,就是在你所有模組的入口模組處,以下面程式碼為例,將你專案本身的絕對路徑拼入到python 的系統path下,這樣自定義的包一定不會出錯!!!

import sys, os
base_path = os.path.dirname(os.path.dirname(
                            os.path.abspath(__file__)))
sys.path.append(base_path)

有想學python的同學,歡迎關注公號:

在這裡插入圖片描述