18.Python編程:模塊深入學(xué)習(xí)

目錄

本文將在學(xué)習(xí)Python中模塊的概念的基礎(chǔ)上悠抹,通過一些示例來繼續(xù)學(xué)習(xí)模塊標(biāo)準(zhǔn)模板、import孟辑、from…import 哎甲、深入理解模塊、__name__屬性饲嗽、包等知識(shí)炭玫。

模塊標(biāo)準(zhǔn)模板

在了解了Python中的模塊知識(shí)以后,就來看一下Python中的模塊標(biāo)準(zhǔn)模板貌虾。例子中模塊名:17my_module.py吞加。
如下:

# encoding: utf-8
#! F:\python_projects\17my_module.py
# 文件名: 17my_module.py

"""
Here is document note
這里是文檔注釋
"""

__author__ = 'Guang TouQiang'


import sys

print('命令行參數(shù)如下:')
for i in sys.argv:
    print(i)

print('\n\nPython 路徑為:', sys.path, '\n')

解釋如下:

第1、第2和第3行是標(biāo)準(zhǔn)注釋尽狠。其中:
第1行注釋表示當(dāng)前模塊采用的是標(biāo)準(zhǔn)utf-8編碼衔憨。
第2行注釋表示可以讓這個(gè)17my_module.py文件直接在Unix/Linux/Mac上運(yùn)行。
第3行注釋表示本模塊名袄膏;
第4-7行是一個(gè)字符串践图,表示模塊的文檔注釋选调,任何模塊代碼的第一個(gè)字符串都被視為模塊的文檔注釋虐沥;
第8行使用_author_變量把作者寫進(jìn)去囤躁,這樣當(dāng)你公開源代碼后別人就可以看到你的大名常潮;

以上就是Python模塊的標(biāo)準(zhǔn)文件模板,當(dāng)然也可以全部刪掉不寫揖盘,但是眉厨,按標(biāo)準(zhǔn)辦事肯定沒錯(cuò)。

后面開始就是真正的代碼部分兽狭。

你可能注意到了憾股,使用sys模塊的第一步,就是導(dǎo)入該模塊:
import sys
導(dǎo)入sys模塊后箕慧,我們就有了變量sys指向該模塊荔燎,利用sys這個(gè)變量,就可以訪問sys模塊的所有功能销钝。sys模塊有一個(gè)argv變量有咨,用list存儲(chǔ)了命令行的所有參數(shù)。

import 語句

想使用 Python 源文件蒸健,只需在另一個(gè)源文件里執(zhí)行 import 語句座享,語法如下:
import module1[, module2[,... moduleN]
當(dāng)解釋器遇到 import 語句,如果模塊在當(dāng)前的搜索路徑就會(huì)被導(dǎo)入似忧。
搜索路徑是一個(gè)解釋器會(huì)先進(jìn)行搜索的所有目錄的列表渣叛。例如:想要導(dǎo)入模塊my_pi,需要把命令放在腳本的頂端:
my_pi.py源碼如下:

# 割圓求π

# 獲取正2N邊形長度
def get_n(n):
    # 正6邊形邊長等于圓的半徑盯捌,假設(shè)圓的半徑為1淳衙,則邊長也為1
    if n == 6.0:
        print("L6 = 1.0")
        return 1.0
    else:
        a = n / 2
        result = math.sqrt(2 - math.sqrt(4 - math.pow(get_n(a), 2)))
        print(r'內(nèi)接正' + str(n).replace('.0', '') + r'邊形邊長 = ' + str(result))
        return result

# 獲取π
def get_pi(n):
    return n * get_n(n) / 2

在my_module模塊中導(dǎo)入,就可以使用了:

import my_pi

# 獲取π
print(r'π = ' + str(my_pi.get_pi(384*8*8)))

運(yùn)行結(jié)果如下:

L6 = 1.0
內(nèi)接正12邊形邊長 = 0.5176380902050416
內(nèi)接正24邊形邊長 = 0.2610523844401031
內(nèi)接正48邊形邊長 = 0.13080625846028635
內(nèi)接正96邊形邊長 = 0.0654381656435527
內(nèi)接正192邊形邊長 = 0.03272346325297234
內(nèi)接正384邊形邊長 = 0.01636227920787303
內(nèi)接正768邊形邊長 = 0.008181208052471188
內(nèi)接正1536邊形邊長 = 0.004090612582339534
內(nèi)接正3072邊形邊長 = 0.0020453073607051096
內(nèi)接正6144邊形邊長 = 0.00102265381399354
內(nèi)接正12288邊形邊長 = 0.0005113269236068993
內(nèi)接正24576邊形邊長 = 0.0002556634639747083
π = 3.1415926453212157

一個(gè)模塊只會(huì)被導(dǎo)入一次饺著,不管你執(zhí)行了多少次import箫攀。這樣可以防止導(dǎo)入模塊被一遍又一遍地執(zhí)行。
當(dāng)我們使用import語句的時(shí)候幼衰,Python解釋器是怎樣找到對(duì)應(yīng)的文件的呢靴跛?

這就涉及到Python的搜索路徑,搜索路徑是由一系列目錄名組成的渡嚣,Python解釋器就依次從這些目錄中去尋找所引入的模塊梢睛。這看起來很像環(huán)境變量,事實(shí)上识椰,也可以通過定義環(huán)境變量的方式來確定搜索路徑绝葡。搜索路徑是在Python編譯或安裝的時(shí)候確定的,安裝新的庫應(yīng)該也會(huì)修改腹鹉。

from…import 語句

Python的from語句讓你從模塊中導(dǎo)入一個(gè)指定的部分到當(dāng)前命名空間中藏畅,語法如下:
from modname import name1[, name2[, ... nameN]]

例如:my_pi.py模塊中,添加了兩個(gè)函數(shù)种蘸,get_standard_pi墓赴、get_simple_pi源碼如下:

# 割圓求π

# 獲取正2N邊形長度
def get_n(n):
    # 正6邊形邊長等于圓的半徑,假設(shè)圓的半徑為1航瞭,則邊長也為1
    if n == 6.0:
        print("L6 = 1.0")
        return 1.0
    else:
        a = n / 2
        result = math.sqrt(2 - math.sqrt(4 - math.pow(get_n(a), 2)))
        print(r'內(nèi)接正' + str(n).replace('.0', '') + r'邊形邊長 = ' + str(result))
        return result


# 獲取標(biāo)準(zhǔn)pi
def get_standard_pi():
    return 3.1415926


# 獲取簡單Pi
def get_simple_pi():
    return 3.14


# 利用割圓方法獲取π
def get_pi(n):
    return n * get_n(n) / 2
錯(cuò)誤示例

在模塊my_module中诫硕,這次只導(dǎo)入get_standard_pi函數(shù)。導(dǎo)入get_standard_pi刊侯,仍調(diào)用my_pi.get_pi()章办,則會(huì)報(bào)錯(cuò)NameError,代碼如下:

from my_pi import get_standard_pi

# 獲取π
print(r'π = ' + str(my_pi.get_pi(384*8*8)))

運(yùn)行結(jié)果:

Traceback (most recent call last):
  File "F:/python_projects/17my_module.py", line 13, in <module>
    print(r'π = ' + str(my_pi.get_pi(384*8*8)))
NameError: name 'my_pi' is not defined

原因是:由于from my_pi import get_standard_pi滨彻,這個(gè)聲明不會(huì)把整個(gè)my_pi模塊導(dǎo)入到當(dāng)前的命名空間中藕届,它只會(huì)將my_pi里的get_standard_pi函數(shù)引入進(jìn)來。而實(shí)際調(diào)用時(shí)亭饵,調(diào)用的get_pi()休偶,所以提示:NameError: name 'my_pi' is not defined,my_pi未定義辜羊。

正確示例

在模塊my_module中踏兜,這次只導(dǎo)入get_standard_pi()函數(shù)。導(dǎo)入get_standard_pi八秃,仍調(diào)用my_pi.get_standard_pi()碱妆,代碼如下:

from my_pi import get_standard_pi

# 獲取π
print(r'π = ' + str(get_standard_pi()))

運(yùn)行結(jié)果:

π = 3.1415926

From…import* 語句

把一個(gè)模塊的所有內(nèi)容全都導(dǎo)入到當(dāng)前的命名空間也是可行的,只需使用如下聲明:
from module_name import *
這提供了一個(gè)簡單的方法來導(dǎo)入一個(gè)模塊中的所有項(xiàng)目昔驱。然而這種聲明不該被過多地使用疹尾,此處也不再舉例。

深入理解模塊

每個(gè)模塊有各自獨(dú)立的符號(hào)表骤肛,在模塊內(nèi)部為所有的函數(shù)當(dāng)作全局符號(hào)表來使用纳本。所以,模塊的作者可以放心大膽的在模塊內(nèi)部使用這些全局變量腋颠,而不用擔(dān)心把其他用戶的全局變量搞污染饮醇。

在一個(gè)模塊(或者腳本,或者其他地方)的最前面使用 import 來導(dǎo)入一個(gè)模塊秕豫,當(dāng)然這只是一個(gè)慣例朴艰,而不是強(qiáng)制的。被導(dǎo)入的模塊的名稱將被放入當(dāng)前操作的模塊的符號(hào)表中混移。
例如:上面例子中的

import my_pi

這樣做并沒有把直接定義在my_pi中的函數(shù)名稱寫入到當(dāng)前符號(hào)表里祠墅,只是把模塊my_pi的名字寫到了那里。


還有一種導(dǎo)入的方法歌径,可以使用 import 直接把模塊內(nèi)(函數(shù)毁嗦,變量的)名稱導(dǎo)入到當(dāng)前操作模塊。比如:

from my_pi import get_standard_pi

這種導(dǎo)入的方法不會(huì)把被導(dǎo)入的模塊的名稱放在當(dāng)前的字符表中(所以在這個(gè)例子里面回铛,my_pi這個(gè)名稱是沒有定義的)狗准。


這還有一種方法克锣,可以一次性的把模塊中的所有(函數(shù),變量)名稱都導(dǎo)入到當(dāng)前模塊的字符表:

from module_name import *

這將把所有的名字都導(dǎo)入進(jìn)來腔长,但是那些由單一下劃線_開頭的名字不在此例袭祟。大多數(shù)情況, Python程序員不使用這種方法捞附,因?yàn)橐氲钠渌鼇碓吹拿砣椋芸赡芨采w了已有的定義。

這也是三種導(dǎo)入方式的重要區(qū)別鸟召。

__name__屬性

一個(gè)模塊被另一個(gè)程序第一次引入時(shí)胆绊,其主程序?qū)⑦\(yùn)行。如果我們想在模塊被引入時(shí)欧募,模塊中的某一程序塊不執(zhí)行压状,我們可以用__name__屬性來使該程序塊僅在該模塊自身運(yùn)行時(shí)執(zhí)行。
基于此跟继,設(shè)計(jì)如下實(shí)例進(jìn)行驗(yàn)證:
我們創(chuàng)建2個(gè)模塊:my_name.py何缓、18name.py,模塊名分別:my_name还栓、18name碌廓;在18name模塊中導(dǎo)入my_name模塊。
其中my_name.py源碼如下:

# 定義一個(gè)測(cè)試函數(shù)
def name_test():
    if __name__ == '__main__':
        print('A: __name__ is __main__')
    else:
        print('B: __name__ is ', __name__)

18name.py源碼如下:

# 導(dǎo)入my_name模塊
import my_name

# 調(diào)用my_name模塊中的name_test函數(shù)
my_name.name_test()

運(yùn)行模塊18name剩盒,運(yùn)行結(jié)果如下:

B: __name__ is  my_name

修改:my_name.py源碼如下:

# 定義一個(gè)測(cè)試函數(shù)
def name_test():
    if __name__ == '__main__':
        print('A: __name__ is __main__')
    else:
        print('B: __name__ is ', __name__)

# 在本模塊中調(diào)用定義好的測(cè)試函數(shù)
name_test()

運(yùn)行模塊my_name谷婆,運(yùn)行結(jié)果如下:

A: __name__ is __main__

說明: 每個(gè)模塊都有一個(gè)__name__屬性,當(dāng)其值是'__main__'時(shí)辽聊,表明該模塊自身在運(yùn)行纪挎,否則是被引入。
提示:__name__ 與 __main__ 底下是雙下劃線跟匆, _ _ 是這樣去掉中間的那個(gè)空格异袄。

通過前面的學(xué)習(xí),我們已經(jīng)知道玛臂,在解決和其他人的模塊同名時(shí)烤蜕,我們引入了包。此時(shí)迹冤,模塊的名字就變成了:包名.模塊名讽营。

注意:
目錄只有包含一個(gè)叫做__init__.py的文件才會(huì)被認(rèn)作是一個(gè)包,主要是為了避免一些濫俗的名字(比如叫做 string)不小心的影響搜索路徑中的有效模塊泡徙。
最簡單的情況橱鹏,放一個(gè)空的 __init__.py就可以了。當(dāng)然這個(gè)文件中也可以包含一些初始化代碼或者為(將在后面介紹的) __all__變量賦值。

注意當(dāng)使用from package import item這種形式的時(shí)候莉兰,對(duì)應(yīng)的item既可以是包里面的子模塊(子包)挑围,或者包里面定義的其他名稱,比如函數(shù)糖荒,類或者變量杉辙。

import語法會(huì)首先把item當(dāng)作一個(gè)包定義的名稱,如果沒找到寂嘉,再試圖按照一個(gè)模塊去導(dǎo)入奏瞬。如果還沒找到枫绅,恭喜泉孩,一個(gè)ImportError異常被拋出了。
反之并淋,如果使用形如import item.subitem.subsubitem這種導(dǎo)入形式寓搬,除了最后一項(xiàng),都必須是包县耽,而最后一項(xiàng)則可以是模塊或者是包句喷,但是不可以是類,函數(shù)或者變量的名字兔毙。

從一個(gè)包中導(dǎo)入*

使用形如from package.xxx import *時(shí)唾琼,Python會(huì)做哪些事情呢?

Python 會(huì)進(jìn)入文件系統(tǒng)澎剥,找到這個(gè)包里面所有的子模塊锡溯,一個(gè)一個(gè)的把它們都導(dǎo)入進(jìn)來。
但是很不幸哑姚,這個(gè)方法在 Windows平臺(tái)上工作的就不是非常好祭饭,因?yàn)閃indows是一個(gè)大小寫不區(qū)分的系統(tǒng)。
在這類平臺(tái)上叙量,沒有人敢擔(dān)保一個(gè)叫做 ECHO.py 的文件導(dǎo)入為模塊 echo 還是 Echo 甚至 ECHO倡蝙。
(例如,Windows 95就很討厭的把每一個(gè)文件的首字母大寫顯示)而且 DOS 的 8+3 命名規(guī)則對(duì)長模塊名稱的處理會(huì)把問題搞得更糾結(jié)绞佩。

為了解決這個(gè)問題寺鸥,只能煩勞包的作者提供一個(gè)精確的包的索引了。

導(dǎo)入語句遵循如下規(guī)則:
如果包定義文件__init__.py 存在一個(gè)叫做__all__ 的列表變量品山,那么在使用 from package import * 的時(shí)候就把這個(gè)列表中的所有名字作為包內(nèi)容導(dǎo)入析既。
作為包的作者,可別忘了在更新包之后保證__all__也更新了啊谆奥。你說我就不這么做眼坏,我就不使用導(dǎo)入*這種用法,好吧,沒問題宰译,誰讓你是老板呢檐蚜。

例如:我們項(xiàng)目中有一個(gè)登陸包,包名:login沿侈。


如上圖闯第,在company_project/login/__init__.py中包含如下代碼:
__all__ = ["abc", "cde", "xyz"]
這表示當(dāng)你使用from company_project.login import *這種用法時(shí),你只會(huì)導(dǎo)入包里面這三個(gè)子模塊缀拭。

如果 __all__ 真的沒有定義咳短,那么使用from company_project.login import *這種語法的時(shí)候,就不會(huì)導(dǎo)入包 login 里的任何子模塊蛛淋。他只是把包c(diǎn)ompany_project.login和它里面定義的所有內(nèi)容導(dǎo)入進(jìn)來(可能運(yùn)行__init__.py里定義的初始化代碼)咙好。這會(huì)把 __init__.py 里面定義的所有名字導(dǎo)入進(jìn)來。并且他不會(huì)破壞掉我們?cè)谶@句話之前導(dǎo)入的所有明確指定的模塊褐荷。

通常我們并不主張使用*這種方法來導(dǎo)入模塊勾效,因?yàn)檫@種方法經(jīng)常會(huì)導(dǎo)致代碼的可讀性降低。不過這樣倒的確是可以省去不少敲鍵的功夫叛甫,而且一些模塊都設(shè)計(jì)成了只能通過特定的方法導(dǎo)入层宫。

記住,使用from Package import specific_submodule這種方法永遠(yuǎn)不會(huì)有錯(cuò)其监。事實(shí)上萌腿,這也是推薦的方法。除非是你要導(dǎo)入的子模塊有可能和其他包的子模塊重名抖苦。

如果在結(jié)構(gòu)中login包是一個(gè)子包(比如這個(gè)例子中對(duì)于包company_project來說)毁菱,而你又想導(dǎo)入兄弟包(同級(jí)別的包,圖中沒有睛约,假設(shè)有個(gè)網(wǎng)絡(luò)請(qǐng)求的network包)你就得使用導(dǎo)入絕對(duì)的路徑來導(dǎo)入鼎俘。比如,如果模塊company_project.login 要使用包company_project.network中的模塊query辩涝,你就要寫成 from company_project.network import query贸伐。

無論是隱式的還是顯式的相對(duì)導(dǎo)入都是從當(dāng)前模塊開始的。主模塊的名字永遠(yuǎn)是"__main__"怔揩,一個(gè)Python應(yīng)用程序的主模塊捉邢,應(yīng)當(dāng)總是使用絕對(duì)路徑引用。

包還提供一個(gè)額外的屬性__path__商膊。這是一個(gè)目錄列表伏伐,里面每一個(gè)包含的目錄都有為這個(gè)包服務(wù)的__init__.py,你得在其他__init__.py被執(zhí)行前定義哦晕拆∶牯幔可以修改這個(gè)變量,用來影響包含在包里面的模塊和子包。

說明: 這個(gè)功能并不常用吝镣,一般用來擴(kuò)展包里面的模塊堤器。

小結(jié)

本文在學(xué)習(xí)Python3中模塊概念的基礎(chǔ)上,通過一些示例來繼續(xù)學(xué)習(xí)了模塊標(biāo)準(zhǔn)模板末贾、import闸溃、from…import 、深入理解模塊拱撵、__name__屬性辉川、包等知識(shí)。

當(dāng)前所在位置(局部視圖)

當(dāng)前所在位置(局部視圖)

當(dāng)前所在位置(全局視圖)

當(dāng)前所在位置(全局視圖)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拴测,一起剝皮案震驚了整個(gè)濱河市乓旗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昼扛,老刑警劉巖寸齐,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欲诺,死亡現(xiàn)場(chǎng)離奇詭異抄谐,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)扰法,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門蛹含,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人塞颁,你說我怎么就攤上這事浦箱。” “怎么了祠锣?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵酷窥,是天一觀的道長。 經(jīng)常有香客問我伴网,道長蓬推,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任澡腾,我火速辦了婚禮沸伏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘动分。我一直安慰自己毅糟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布澜公。 她就那樣靜靜地躺著姆另,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迹辐,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天苟蹈,我揣著相機(jī)與錄音,去河邊找鬼右核。 笑死慧脱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的贺喝。 我是一名探鬼主播菱鸥,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼躏鱼!你這毒婦竟也來了氮采?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤染苛,失蹤者是張志新(化名)和其女友劉穎鹊漠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茶行,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躯概,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了畔师。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娶靡。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖看锉,靈堂內(nèi)的尸體忽然破棺而出姿锭,到底是詐尸還是另有隱情,我是刑警寧澤伯铣,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布呻此,位于F島的核電站,受9級(jí)特大地震影響腔寡,放射性物質(zhì)發(fā)生泄漏焚鲜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一蹬蚁、第九天 我趴在偏房一處隱蔽的房頂上張望恃泪。 院中可真熱鬧,春花似錦犀斋、人聲如沸贝乎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽览效。三九已至却舀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锤灿,已是汗流浹背挽拔。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留但校,地道東北人螃诅。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像状囱,于是被迫代替她去往敵國和親术裸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容