今天咱們來聊聊模塊和包python
一.模塊mysql
首先,咱們先看一個老生常談的問題,什麼是模塊,模塊就是一個包含了python定義和聲明的文件,文件名就是模塊的名字加上.py後綴,歡聚話說咱們目前寫的全部的py文件均可以當作是一個模塊可是咱們import加載的模塊一共分紅四個通用類別:sql
1. 使用pyhton編寫的py文件json
2. 已被變異爲共享庫或者DLL或C或者C++的擴展api
3. 包好一組模塊的包. app
4. 使用c編寫並鏈接到python解釋器的內置模塊dom
爲何要使用模塊? 爲了咱們寫的代碼能夠重用,不至於把全部的代碼都寫在一個文件內. 當項目規模比較小的時候,徹底可使用一個py搞定整個項目的開發,可是若是是一個很是龐大的項目. 此時就必需要把相關的功能進行分離,以便咱們的平常維護,以及新項目的開發. 函數
如何使用模塊? 咱們已用過不少模塊了,導入模塊有兩種方式學習
1. import 模塊測試
2. from xxx import xxxx
二.import
首先咱們先看import, 在使用import的時候, 咱們先建立一個yitian.py. 在該文件中建立一些武林前輩和一些打鬥場景, 代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
print
(
"片頭曲. 啊! 啊~ 啊! 啊. 啊啊啊啊啊啊啊..."
)
main_person_man
=
"張無忌"
main_person_woman
=
"趙敏"
low_person_man_one
=
"成昆"
low_person_man_two
=
"周芷若"
def
fight_on_light_top():
print
(
"光明頂大戰"
, main_person_man,
"破壞了"
, low_person_man_one,
"的大陰謀"
)
def
fight_in_shaolin():
print
(
"少林寺大戰"
, main_person_man,
"破壞了"
, low_person_man_two, "的大陰
謀")
|
接下來,金庸上場
1
2
3
4
5
6
7
|
import
yitian
print
(yitian.main_person_man)
# 使用模塊中定義好的名字
print
(yitian.low_person_man_one)
yitian.fight_in_shaolin()
# 調用模塊中的函數
yitian.fight_on_light_top()
|
此時咱們在金庸模塊中引入了yitian模塊.
在Python中模塊是不可以重複導入的,當重複導入模塊時,系統會根據sys.modules來判斷該模塊是否已經導入了,若是已經導入,則不會重複導入
1
2
3
4
5
6
7
8
9
|
import
sys
print
(sys.modules.keys())
# 查看導入的模塊.
import
yitian
# 導入模塊. 此時會默認執行該模塊中的代碼
import
yitian
# 該模塊已經導入過了,不會重複執行代碼
import
yitian
import
yitian
import
yitian
import
yitian
|
導入模塊的時候都作了些什麼? 首先,在導入模塊的一瞬間,python解釋器會先經過sys.modules來判斷該模塊是否已經導入了該模塊,若是已經導入了則再也不導入,若是該模塊還未導入過,則系統會作三件事.
1. 爲導入的模塊創立新的名稱空間
2. 在新建立的名稱空間中運行該模塊中的代碼
3. 建立模塊的名字,並使用該名稱做爲該模塊在當前模塊中引用的名字.
咱們可使用globals來查看模塊的名稱空間
1
2
3
4
5
6
7
8
9
|
print
(
globals
())
打印結果:
{
'__name__'
:
'__main__'
,
'__doc__'
:
None
,
'__package__'
:
None
,
'__loader__'
: <_frozen_importlib_external.SourceFileLoader
object
at
0x10bbcf438
>,
'__spec__'
:
None
,
'__annotations__'
: {},
'__builtins__'
:
<module
'builtins'
(built
-
in
)>,
'__file__'
:
'/Users/sylar/PycharmProjects/oldboy/模塊/模塊/⾦庸.py'
,
'__cached__'
:
None
,
'yitian'
: <module
'yitian'
from
'
/
Users
/
sylar
/
PycharmProjects
/
oldboy
/
模塊
/
模
塊
/
yitian.py
'>, '
sys
': <module '
sys' (built
-
in
)>}
|
注意,因爲模塊在導入的時候會建立其本身的名稱空間,因此咱們在使用模塊中的變量的時候通常是不會產生衝突的.
1
2
3
4
5
6
7
8
9
10
11
|
import
yitian
main_person_man
=
"胡一菲"
def
fight_in_shaolin():
print
(main_person_man,
"大戰曾小賢"
)
print
(yitian.main_person_man)
# 張無忌
print
(main_person_man)
# 胡一菲
yitian.fight_in_shaolin()
# 倚天屠龍記中的
fight_in_shaolin()
# 本身的
|
注意,在模塊中使用global,咱們以前說global表示把全局的內容引入到局部,可是這個全局指的是py文件,換句話說,global指向的是模塊內部,並不會改變外部模塊的內容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
模塊 yitian 中:
print
(
"片頭曲. 啊! 啊~ 啊! 啊. 啊啊啊啊啊啊啊..."
)
main_person_man
=
"張無忌"
main_person_woman
=
"趙敏"
low_person_man_one
=
"成昆"
low_person_man_two
=
"周芷若"
def
fight_on_light_top():
print
(
"光明頂大戰"
, main_person_man,
"破壞了"
, low_person_man_one,
"的大陰謀"
)
def
fight_in_shaolin():
global
low_person_man_two
# 注意看, 此時的global是當前模塊. 並不會影響
其餘模塊
low_person_man_two
=
"戰五渣"
print
(
"少林寺大戰"
, main_person_man,
"破壞了"
, low_person_man_two,
"的大陰謀"
)
調用方:
import
yitian
low_person_man_two
=
"劉海柱"
yitian.fight_in_shaolin()
print
(yitian.low_person_man_two)
# 戰五渣
print
(low_person_man_two)
# 劉海柱. 並無改變當前模塊中的內容. 因此模塊內部的
global
只是用於模塊內部
|
特別特別要注意,若是咱們在不一樣的模塊中引入了同一個模塊,而且在某一個模塊中改變了被引入模塊中的全局變量,則其餘模塊看到的值也跟着變,緣由是python的模塊只會引入一次,你們共享同一個名稱空間
1
2
3
4
5
6
7
8
9
|
金庸:
import
yitian
yitian.main_person_man
=
"滅絕師太"
金庸二號:
import
yitian
import
金庸
print
(yitian.main_person_man)
# 滅絕師太.
|
上述問題出現的緣由:
1. 你們共享同一個模塊的名稱空間.
2. 在金庸裏改變了主⻆的名字
如何解決呢?
首先, 咱們不能去改python,由於python的規則不是咱們定的,只能想辦法不要改變主⻆的名字,可是在金庸裏我就有這樣的需求,那此時就出現了,在金庸被執行的時候要執行的代碼,在金庸被別人導入的時候咱們不想執行這些代碼,此時咱們就要利用一下__name__這個內置變量了. 在Python中,每一個模塊都有本身的__name__ ,可是這個__name__的值是不定的,當咱們把一個模塊做爲程序運行的入口時,此時該模塊的__name__是"__main__" , 而若是咱們把模塊導入時,此時模塊內部的__name__就是該模塊自身的名字
1
2
3
4
5
6
7
|
金庸:
print
(__name__)
# 此時若是運行該文件,則__name__是__main__
金庸二號:
import
金庸
#此時打印的結果是"金庸"
|
咱們能夠利用這個特性來控制模塊內哪些代碼是在被加載的時候就運行的,哪些是在模塊被別人導入的時候就要執行的,也能夠屏蔽掉一些不但願別人導入就運行的代碼,尤爲是測試代碼.
1
2
3
4
5
|
if
__name__
=
=
'__main__'
:
yitian.main_person_man
=
"滅絕師太"
# 此時, 只有從該模塊做爲入口運行的時候才
會把main_person_man設置成滅絕師太
print
(
"哇哈哈哈哈哈"
)
# 只有運行該模塊纔會打印,import的時候是不會執行這裏的代
碼的
|
咱們還能夠對導入的模塊進行從新命名:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
yitian as yt
# 導入yitian. 可是名字被從新命名成了yt. 就比如變量賦值同樣.
a
=
1
b
=
a
yt.fight_in_shaolin()
# 此時能夠正常運行
# yitian.fight_in_shaolin() # 此時程序報錯. 由於引入的yitian被重命名成了yt
print
(
globals
())
{
'__name__'
:
'__main__'
,
'__doc__'
:
None
,
'__package__'
:
None
,
'__loader__'
: <_frozen_importlib_external.SourceFileLoader
object
at
0x103209438
>,
'__spec__'
:
None
,
'__annotations__'
: {},
'__builtins__'
:
<module
'builtins'
(built
-
in
)>,
'__file__'
:
'/Users/sylar/PycharmProjects/oldboy/模塊/模塊/金庸.py'
,
'__cached__'
:
None
,
'yt'
: <module
'yitian'
from
'
/
Users
/
sylar
/
PycharmProjects
/
oldboy
/
模塊
/
模
塊
/
yitian.py'>}
|
一次能夠引入多個模塊
1
|
import
time, random, json, yitian
|
正確的導入模塊的順序:
1. 全部的模塊導入都要寫在最上面,這是最基本的
2. 先引入內置模塊
3. 再引入擴展模塊
4. 最後引入你本身定義的模塊
三. from xxx import xxx
第一大塊關於import就說這麼多,接下來咱們來看from xxx import xxx這種導入模塊的效果,在使用from的時候, python也會給咱們的模塊建立名稱空間,這一點和import是同樣的,可是from xxx import xxx的時候,咱們是把這個空間中的一些變量引入過來了,說白了就是部分導入,當一個模塊中的內容過多的時候,咱們能夠選擇性的導入要使用的內容.
1
2
|
from
yitian
import
fight_in_shaolin
fight_in_shaolin()
|
此時是能夠正常運行的,可是咱們省略了以前的模塊.函數() 直接函數()就能夠執行了, 而且from語句也支持一行語句導入多個內容
1
2
3
4
|
from
yitian
import
fight_in_shaolin, fight_on_light_top, main_person_man
fight_in_shaolin()
fight_on_light_top()
print
(main_person_man)
|
一樣支持as
1
2
3
4
5
|
from
yitian
import
fight_in_shaolin, fight_on_light_top, main_person_man as
big_lao
fight_in_shaolin()
fight_on_light_top()
print
(big_lao)
|
最後看一下from的坑,當咱們從一個模塊中引入一個變量的時候,若是當前文件中出現了重名的變量時,會覆蓋掉模塊引入的那個變量.
1
2
3
|
from
yitian
import
main_person_man
main_person_man
=
"超級大滅絕"
print
(main_person_man)
|
因此,不要重名,切記,不要重名! 不只僅是變量名不要重複,咱們本身建立的py文件的名字不要和系統內置的模塊重名,不然引入的模塊都是python內置的模塊. 切記, 切記.
咱們如今知道可使用import和from xxx import xxx來導入一個模塊中的內容,那有一種特殊的寫法: from xxx import * 咱們說此時是把模塊中的全部內容都導入, 注意,若是模塊中沒有寫出__all__ 則默認全部內容都導入,若是寫了__all__ 此時導入的內容就是在__all__列表中列出來的全部名字.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# haha.py
__all__
=
[
"money"
,
"chi"
]
money
=
100
def
chi():
print
(
"我是吃"
)
def
he():
print
(
"我是呵呵"
)
# test.py
from
haha
import
*
chi()
print
(money)
# he() # 報錯
|
四.包
包是一種經過 '.模塊名'來組織python模塊名稱空間的方式,那什麼樣的東西是包呢? 咱們建立的每一個文件夾均可以被稱之爲包,可是咱們要注意, 在python2中規定,包內必須存在__init__.py文件,建立包的目的不是爲了運行, 而是被導入使用,包只是一種形式而已,包的本質就是一種模塊
爲什麼要使用包? 包的本質就是一個文件夾, 那麼文件夾惟一的功能就是將文件組織起來,隨着功能越寫越多, 咱們沒法將全部功能都放在一個文件中, 因而咱們使用模塊去組織功能隨着模塊愈來愈多, 咱們就須要用文件夾將模塊文件組織起來, 以此來提升程序的結構性和可維護性
首先, 咱們先建立一些包,用來做爲接下來的學習,包很好建立,只要是一個文件夾, 有__init__.py就能夠
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
os
os.makedirs(
'glance/api'
)
os.makedirs(
'glance/cmd'
)
os.makedirs(
'glance/db'
)
l
=
[]
l.append(
open
(
'glance/__init__.py'
,
'w'
))
l.append(
open
(
'glance/api/__init__.py'
,
'w'
))
l.append(
open
(
'glance/api/policy.py'
,
'w'
))
l.append(
open
(
'glance/api/versions.py'
,
'w'
))
l.append(
open
(
'glance/cmd/__init__.py'
,
'w'
))
l.append(
open
(
'glance/cmd/manage.py'
,
'w'
))
l.append(
open
(
'glance/db/__init__.py'
,
'w'
))
l.append(
open
(
'glance/db/models.py'
,
'w'
))
map
(
lambda
f:f.close() ,l)
|
建立好目錄結構
咱們接下來給每一個文件中添加一些方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#policy.py
def
get():
print
(
'from policy.py'
)
#versions.py
def
create_resource(conf):
print
(
'from version.py: '
,conf)
#manage.py
def
main():
print
(
'from manage.py'
)
#models.py
def
register_models(engine):
print
(
'from models.py: '
,engine)
|
接下來咱們在test中使用包中的內容,而且咱們導入包的時候可使用import或者from xxx import xxx這種形式.
首先, 咱們看import
1
2
|
import
glance.db.models
glance.db.models.register_models(
'mysql'
)
|
沒問題, 很簡單, 咱們還可使用from xxx import xxx 來導入包內的模塊
1
2
|
from
glance.api.policy
import
get
get()
|
也很簡單, 可是要注意,from xxx import xxx這種形式, import後面不能夠出現"點" 也就是說from a.b import c是ok的,可是 from a import b.c 是錯誤的
好了, 到目前爲止, 簡單的包已經可使用了,那包裏的__init__.py是什麼鬼? 其實不論咱們使用哪一種方式導入一個包, 只要是第一次導入包或者是包的任何其餘部分, 都會先執行__init__.py文件,這個文件能夠是空的,但也能夠存放一些初始化的代碼. (隨意在glance中的__init__.py均可以進行測試)
那咱們以前用的from xxx import *還能夠用麼? 能夠,咱們要在__init__.py文件中給出_all__來肯定* 導入的內容.
1
2
3
4
5
6
7
8
9
|
print
(
"我是glance的__init__.py文件. "
)
x
=
10
def
hehe():
print
(
"我是呵呵"
)
def
haha():
print
(
"我是哈哈"
)
__all__
=
[
'x'
,
"hehe"
]
|
test.py
1
2
3
4
5
|
from
glance
import
*
print
(x)
# OK
hehe()
# OK
haha()
# 報錯. __all__裏沒有這個鬼東西
|
接下來, 咱們來看一下絕對導入和相對導入, 咱們的最頂級包glance是寫給別人用的,而後再glance包內部也會有彼此之間互相導入的需求, 這時候就有絕對導入和相對導入兩種方式了.
1. 絕對導入: 以glance做爲起始
2. 相對導入: 用. 或者..做爲起始
例如, 咱們在glance/api/version.py中使用glance/cmd/manage.py
1
2
3
4
5
6
7
8
9
10
11
|
# 在glance/api/version.py
#絕對導入
from
glance.cmd
import
manage
manage.main()
#相對導入
# 這種情形不能夠在versions中啓動程序.
# attempted relative import beyond top-level package
from
..cmd
import
manage
manage.main()
|
測試的時候要注意,python包路徑跟運行腳本所在的目錄有關係,說白了就是你運行的py文件所在的目錄,在python中不容許你運行的程序導包的時候超過當前包的範圍(相對導入). 若是使用絕對導入,沒有這個問題,換個說法,若是你在包內使用了相對導入,那在使用該包內信息的時候,只能在包外面導入.
接下來咱們來看一個大坑,好比,咱們想在policy中使用verson中的內容.
1
2
|
# 在policy.py
import
versions
|
若是咱們程序的入口是policy.py 那此時程序是沒有任何問題的,可是若是咱們在glance外面import了glance中的policy就會報錯 ,緣由是若是在外面訪問policy的時候,sys.path中的路徑就是外面, 因此根本就不能直接找到versions模塊,因此必定會報錯:
1
|
ModuleNotFoundError: No module named
'versions'
|
在導包出錯的時候,必定要先看sys.path 看一下是否真的能獲取到包的信息.
最後, 咱們看一下如何單獨導入一個包.
1
2
|
# 在test.py中
import
glance
|
此時導入的glance什麼都作不了,由於在glance中的__init__.py中並無關於子包的加載,此時咱們須要在__init__.py中分別去引入子包中的內容.
1. 使用絕對路徑
2. 使用相對路徑
包的注意事項:
1. 關於包相關的導入語句也分爲import和from xxx import xxx兩種, 但不管使用哪一種,不管在什麼位置, 在導入時都必須遵循一個原則: 凡是在導入時d帶點的,點左邊都必須是一個包,不然報錯,能夠帶一連串的點, 好比a.b.c
2. import導入文件時,產生名稱空間中的名字來源於文件, import 包, 產生的名稱空間中的名字一樣來源於文件, 即包下的__init__,py, 導入包本質就是在導入該文件
3. 包A和包B下有同名模塊也不會衝突, 如A.a和B.a來自兩個名稱空間