一、函數(shù) 函數(shù)的作用:可以計算出一個返回值,最大化代碼重用,最小化代碼冗余,流程的分解。 1、函數(shù)相關(guān)的語句和表達(dá)式 語句 例子 Calls myfunc(‘diege','eggs',meat=‘lit’) #使用函數(shù) def,return,yield def adder(a,b=1,*c): return a+b+c[0] global changer(): global x;x='new' lambda Funcs=[lambad x:x**2,lambad x:x*3] 2、編寫函數(shù) def創(chuàng)建了一個對象并將其賦值給某一個變量名。 return將一個結(jié)果對象發(fā)送給調(diào)用者。 函數(shù)是通過賦值(對象引用)傳遞的。 參數(shù)通過賦值傳遞給函數(shù)。 global聲明了一個模塊級的變量并被賦值。 參數(shù),返回值以及變量并不是聲明 def <name>(arg1,age2,...,agrN): <statements> return <value 函數(shù)主體往往都包含了一個return語句(不是必須的),如果它沒有出現(xiàn),那么函數(shù)將會在控制流執(zhí)行完函數(shù)主體時結(jié)束,技術(shù)上,沒有返回值的函數(shù)自動返回了none對象。 return可以在函數(shù)主體中的任何地方出現(xiàn)。它表示函數(shù)調(diào)用的結(jié)束,并將結(jié)果返回至函數(shù)調(diào)用。 不要嘗試判斷傳入?yún)?shù)的對象類型,這樣實質(zhì)上破壞了函數(shù)的靈活性,把函數(shù)限制在特定的類型上 3、作用域 作用域:變量定義以及查找的地方。 本地變量:在函數(shù)內(nèi)定義的變量為本地變量。 全局變量:在文件內(nèi)[模塊內(nèi)]函數(shù)外定義的變量為全局變量 Python創(chuàng)建,改變或者查找變量名都是在所謂的命名空間(一個保存變量名的地方,模塊中為__main__)中進(jìn)行。作用域這個術(shù)語指的就是命名空間。 也就說,在代碼中變量名被賦值的位置決定了這個變量名能被訪問到的范圍 一個函數(shù)所有變量名都與函數(shù)的命名空間相關(guān)聯(lián)。 *def內(nèi)定義變量名def內(nèi)使用,是本地變量 *def之中的變量名與def之外的變量名不發(fā)生沖突,使用別處相同的變量名也沒問題。 函數(shù)定義了本地作用域,而模塊定義了全局作用域。兩作用域關(guān)系。 *內(nèi)嵌的模塊是全局作用域:對于外部的全局變量就成為了一個模塊對象的屬性 *全局作用域的作用范圍僅限單個文件:不要被全局迷惑,這里的全局是一個文件的頂層的變量名,僅對這個文件內(nèi)部的代碼而言是全局。 Python中,沒有一個無所不包的情景文件作用域。替代的方法是,變量名由模塊文件隔開,必須精準(zhǔn)地導(dǎo)入一個模塊文件才能夠使用這文件中 定義的變量名, *每次對函數(shù)的調(diào)用都創(chuàng)建了一個新的本地作用域。 *賦值的變量名廢除聲明為全局變量,否則均為本地變量。 *所用的變量名都可以歸納為本地,全局,或者內(nèi)置。(內(nèi)置:ptyhon預(yù)定義的__builtin__模塊提供的) 變量名解析:LEGB原則 對一個def語句 *變量名引用分為三個作用域進(jìn)行查找:首先查找本地,然后是函數(shù)內(nèi)(如果有),之后全局,最后內(nèi)置。 *默認(rèn)情況下,變量名賦值會創(chuàng)建或改變本地變量 *全局聲明將賦值變量名映射到模塊文件內(nèi)部的作用域。 global global語句包含關(guān)鍵字global *全局變量是位于模塊文件內(nèi)部頂層的變量名 *全局變量如果是在函數(shù)內(nèi)部被賦值的話,并需經(jīng)過聲明 *全局變量名在函數(shù)的內(nèi)部不經(jīng)過聲明也可以被引用 4、傳遞參數(shù) 參數(shù)傳遞:傳遞給函數(shù)作為其輸入對象的方式 *參數(shù)的傳遞是通過自動將對象賦值給本地變量來實現(xiàn)的。 *在函數(shù)內(nèi)部的參數(shù)名的賦值不會影響調(diào)用者。 *改變函數(shù)的可變對象參數(shù)的值也許會對調(diào)用者有影響。 換句話說,因為參數(shù)是簡單的通過賦值進(jìn)行對象的傳遞,函數(shù)能夠改變傳入的可變對象,因此其結(jié)果會影響調(diào)用者。 *不可變參數(shù)是“通過值”進(jìn)行傳遞。 像整數(shù)和字符串這樣的對象是通過對象引用而不是拷貝進(jìn)行傳遞的,但是因為你無論如何都不可能在原處改變不可變對象,實際的效果就是很像創(chuàng)建了一份拷貝。 可變對象是通過“指針”進(jìn)行傳遞的。 避免可變參數(shù)的修改 在Python中,默認(rèn)通過引用(也就是指針)進(jìn)行函數(shù)的參數(shù)傳遞。如果不想在函數(shù)內(nèi)部在原處的修改影響傳遞給它的對象。那么,能夠簡單的創(chuàng)建一個可變對象的拷貝。 我們總是能夠在調(diào)用時對列表進(jìn)行拷貝 L=[1,2] changer(X,L[:]) 如果不想改變傳入的對象,無論函數(shù)是如何調(diào)用的,我們可以在函數(shù)內(nèi)部進(jìn)行拷貝,避免可變參數(shù)的修改 >>> def changer(a,b): ... a=2 ... b=b[:] ... b[0]='diege' 特定參數(shù)匹配模型 參數(shù)在ptyhon中總是通過賦值進(jìn)行傳遞,傳入的對象賦值給了在def頭部的變量名。盡管這樣,在模型的上層,python提供了額外的工具,該工具改變了調(diào)用過中 賦值時參數(shù)對象匹配在頭部的參數(shù)名的優(yōu)先級。這些工具是可選的。 總結(jié)與特定模式有關(guān)的語法: 語法 位置 解釋 func(value) 調(diào)用者 常規(guī)參數(shù),通過位置進(jìn)行匹配,從左到右進(jìn)行匹配 func(name=value) 調(diào)用者 關(guān)鍵字參數(shù),通過變量名匹配 func(*name) 調(diào)用者 以name傳遞所有的對象,并作為獨立的基于位置的參數(shù) func(**name) 調(diào)用者 以name成對的傳遞所有的關(guān)鍵字/值,并作為獨立的關(guān)鍵字的參數(shù) def func(name) 函數(shù) 常規(guī)參數(shù):通過位置或變量名進(jìn)行匹配 def func(name=value) 函數(shù) 默認(rèn)參數(shù)值:如果沒有在調(diào)用中傳遞的話,就是用默認(rèn)值 def func(*name) 函數(shù) 匹配并收集(在元組中)所有包含位置的參數(shù) def func(**name) 函數(shù) 匹配并收集(在字典中)所有包含位置的參數(shù)。 5、匿名函數(shù):lamdba lambad 創(chuàng)建了一個之后能夠被調(diào)用的函數(shù),它返回了一個函數(shù)而不是將這個函數(shù)賦值給一個變量名。 lambda表達(dá)式 lanbda arg1,arg2,...,argN:expression using arguments lambda 是一個表達(dá)式,而不是一個語句 lambda的主體是一個單個的表達(dá)式,而不是代碼塊 func(x,y,z):return x+y+z 默認(rèn)參數(shù)也能夠再lambda參數(shù)中使用,就像在def中使用一樣。 lambda a='free',b='file',c='feel':a+b+c 6、作參數(shù)來應(yīng)用函數(shù) 內(nèi)置函數(shù)apply 當(dāng)需要變得更加動態(tài)的話,可以通過將一個函數(shù)作為一個參數(shù)傳遞給apply來調(diào)用一個生成的函數(shù),并且 也將傳給那個函數(shù)的參數(shù)作為一個元組傳遞給apply函數(shù)() apply(函數(shù),參數(shù)1(元組),參數(shù)2(元組)) 7、在序列中映射函數(shù):map 使用內(nèi)置工具map,map函數(shù)會對一個序列對象中的每一個元素應(yīng)用被傳入的函數(shù),并且返回一個包含了所有函數(shù)調(diào)用結(jié)果的一個列表。 map(函數(shù),傳入函數(shù)的序列對象) >>> def inc(x):return x+10 >>> L=[1,2,3,4,5] >>> map(inc,L) [11, 12, 13, 14, 15] >>> L=[1,2,3,4,5] map嵌套lambda >>> map((lambda x:x+3),L) [4, 5, 6, 7, 8] 高級功能:提供了多個序列作為參數(shù),它能夠并行返回分別以每個序列的元素作為【函數(shù)對應(yīng)參數(shù)】得到的結(jié)果的列表 >>> pow(3,4) 81 >>> map(pow,[1,2,3],[2,3,4]) #1**2,2**3,3**4 [1, 8, 81] 8、函數(shù)式編程工具:filter和reduce 函數(shù)式編程的意思就是對序列應(yīng)用一些函數(shù)的工具。 基于某一測試函數(shù)過濾出一些元素-filter 對每對元素都應(yīng)用函數(shù)并運行到最后結(jié)果-reduce >>> filter((lambda x:x>0),range(-5,5)) [1, 2, 3, 4] 這個等效于for range:if語句 reduce稍微復(fù)雜一點。這里兩個reduce調(diào)用,計算在一個列表中所有元素加起來和以及乘起來的乘積 >>> reduce((lambda x,y:x+y),[1,2,3,4]) 10 >>> reduce((lambda x,y:x*y),[1,2,3,4]) 24 依次計算 9、重訪列表解析:映射 1)、列表解析基礎(chǔ) >>> [x**2 for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> map((lambda x:x**2),range(10)) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 2)、增加測試和嵌套循環(huán) 在for之后編寫if分支,用來增加邏輯選擇,if分支相當(dāng)filter >>> [x for x in range(5) if x%2==0] [0, 2, 4] >>> filter((lambda x:x%2==0),range(5)) [0, 2, 4] 3)filter出來的列表可以作為map的第2個參數(shù) >>> map((lambda x:x**2),filter((lambda x:x%2==0),range(5))) [0, 4, 16] 列表解析和map的對比 兩者類似,都會收集對序列或其他可迭代對象中每個元素應(yīng)用元算后的結(jié)果(一次一個項目),從而創(chuàng)建新列表。其主要差異在于。 map會對每個元素應(yīng)用函數(shù),而列表解析則是應(yīng)用任意的表達(dá)式。因此,列表解析更通用一些,可以像map那樣應(yīng)用函數(shù)調(diào)用表達(dá)式, 但是,map需要一個函數(shù)才能應(yīng)用其他種類的表達(dá)式(函數(shù)是map的第一參數(shù)).列表解析也支持?jǐn)U展語法,如果,嵌套for循環(huán)和if 分句從而可以包含內(nèi)置函數(shù)filter的功能。 10、重返迭代器:生成器 編寫的函數(shù)能夠返回一個值,并且稍后還可以從它剛才離開的地方仍然返回值。這樣的函數(shù)被認(rèn)作是生成器,因為它們隨時間生成一個序列的值。 大多數(shù)方面生成器函數(shù)就像一般函數(shù),在Python它們被自動用作實現(xiàn)迭代協(xié)議,因它只能夠再迭代的語境中出現(xiàn)。 生成器和一般的函數(shù)之間代碼上最大的不同就是一個生成器yield一個值,而不是return一個值。yield語句將會將函數(shù)關(guān)起,并向它的調(diào)用者返回一個值 但是保存足夠的狀態(tài)信息為了讓其能夠在函數(shù)從它掛起的地方恢復(fù)。 包含yield的語句的函數(shù)將會特地編譯成為生成器。當(dāng)調(diào)用它時,他們返回一個生成器對象,這個生成器對象支持迭代器對象接口。 >>> def Dtest(N): ... for i in range(N): ... yield i**2 使用 >>> for i in Dtest(5): ... print i,':', ... 0 : 1 : 4 : 9 : 16 : 查看過程 >>> x=Dtest(4) >>> x.next() 0 >>> x.next() 11、函數(shù)設(shè)計概念 *耦合性:對于輸入使用參數(shù),并且對于輸出使用return語句 *耦合性:只有在真正必要的情況下使用全局變量。 *耦合性:不要改變可變類型的參數(shù),除非調(diào)用者希望這樣做。 *聚合性:每一個函數(shù)都應(yīng)該有一個單一的,同一的目標(biāo) *大小:每一個函數(shù)應(yīng)該相對較小。 *耦合:避免直接改變在另一個模塊文件中的變量。 函數(shù)是對象:簡潔調(diào)用 二、模塊 1、基本 每個文件都是一個模塊,并且模塊導(dǎo)入其他模塊之后就可以使用導(dǎo)入模塊定義的變量名。模塊可以由兩個語句和一個重要的內(nèi)置函數(shù)進(jìn)行處理。 import: 使客戶端(導(dǎo)入者)以一個整體獲取一個模塊。 from:容許客戶端從一個模塊文件中獲取特定的變量名。 reload:在不中止Python程序的情況下,提供了一個重新載入模塊文件代碼的方法。 import和from是賦值語句,是可執(zhí)行的語句,可以嵌套到if ,def語句中和def一樣import和from都是隱性賦值語句 在一個模塊文件的頂層定義的所有變量名都成為了被導(dǎo)入的模塊對象的屬性。 模塊至少有三個角色: 代碼重用:模塊還是定義變量名的空間,被認(rèn)作是屬性??梢员欢鄠€外部的客戶端應(yīng)用。 系統(tǒng)命名空間的劃分: 現(xiàn)實共享服務(wù)和數(shù)據(jù): 2、python程序構(gòu)架 import如何工作 執(zhí)行三個步驟 1)、找到模塊文件 2)、編譯成位碼(需要時) 3)、執(zhí)行模塊的代碼來創(chuàng)建其所定義的對象。 在之后導(dǎo)入相同的模塊時候,會跳過這三個步驟,而只提取內(nèi)存中已加載模塊對象。 搜索模塊 導(dǎo)入模塊時,不帶模塊的后綴名,比如.py Python搜索模塊的路徑: 1)、程序的主目錄 2)、PTYHONPATH目錄(如果已經(jīng)進(jìn)行了設(shè)置) 3)、標(biāo)準(zhǔn)連接庫目錄(一般在/usr/local/lib/python2.X/) 4)、任何的.pth文件的內(nèi)容(如果存在的話).新功能,允許用戶把有效果的目錄添加到模塊搜索路徑中去 .pth后綴的文本文件中一行一行的地列出目錄。 這四個組建組合起來就變成了sys.path了, >>> import sys >>> sys.path 導(dǎo)入時,Python會自動由左到右搜索這個列表中每個目錄。 第1,第3元素是自動定義的,第2,第4可以用于擴(kuò)展路徑,從而包括自己的源碼目錄。 3、模塊的創(chuàng)建和使用。 創(chuàng)建模塊 后綴.py文本文件,模塊頂層指定的所有變量名都會變成其屬性。 定義一個module.py模塊 name='diege' age=18 def printer(x): print x 使用模塊 import全部導(dǎo)入,import將整個模塊對象賦值給一個變量名,模塊只導(dǎo)入一次,因為該操作開銷大 >>> import module 屬性 >>> module.name 'diege' 函數(shù) >>> module.printer('hi') hi from語句:from 將獲?。◤?fù)制)模塊特定變量名 from會把變量名賦值到另一個作用域,所以它就可以讓我們直接在腳本中使用復(fù)制后的變量名,而不是通過模塊 from 模塊名 import 需要復(fù)制的屬性 將一個或多個變量名賦值給另一個模塊中同名的對象 from 模塊名 import 需要復(fù)制的屬性 as 新的屬性名 將一個或者多個變量名賦值給另一個模塊中不同名的對象 from * 語句 from 模塊名 import * 取得模塊頂層所有賦了值的變量名的拷貝 >>> from module import name >>> name 'diege >>> from module import name as myname >>> myname 'diege' >>> from module import printer as PR >>> PR('hi python') hi python >>> from module import name,age 復(fù)制多個變量名時要用逗號隔開 >>> name,age ('diege', 18) >>> from module import name as myname,age as myage 復(fù)制多個變量名并改變需時需要用逗號隔開多個as >>> from module import * 4、模塊命名空間 模塊最好理解為變量名的封裝,簡而言之,模塊就是命名空間(變量名建立所在的場所),而存在于模塊之內(nèi)的變量名就是模塊對象的屬性。 文件生成命名空間 *模塊語句會在首次導(dǎo)入時執(zhí)行。 *頂層的賦值語句會創(chuàng)建模塊屬性(文件頂層不在的def和class之內(nèi)的,但def和class隱性創(chuàng)建的變量名也屬于模塊屬性)。賦值的變量名會存儲在模塊的命名空間內(nèi)。 *模塊的命名空間能通過屬性__dict__(module.__dict__)或dir(module)獲取 由于導(dǎo)入而建立的模塊的命名空間是字典,可通過模塊對象相關(guān)聯(lián)的內(nèi)置__dict__屬性讀取。 dir函數(shù)查看,大至與對象的__dict__屬性的鍵排序后的列表相等,但是它還包含了類繼承的變量名。 *模塊是一個獨立的作用域。 5、重載模塊 模塊程序代碼默認(rèn)值對每個過程執(zhí)行一次,要強(qiáng)制使模塊代碼重新載入并重新運算需要使用reload內(nèi)置函數(shù)。 reload是函數(shù),import是語句。兩個語法不一樣。 >>> import module >>> reload(module) <module 'module' from 'module.pyc'> >>> reload(test17) <module 'test17' from '/root/test17.py'> reload()之前需得import過一次 6、模塊包 除模塊名以外,導(dǎo)入也可以指定目錄路徑,Pytyh代碼的目錄就是稱為包。因此這類導(dǎo)入就稱為包導(dǎo)入 import dir1.dir2.mod from dir1.dir2.mod import x .號路徑相當(dāng)于機(jī)器上目錄層次的路徑。 dir1在容器目錄dir0中,dir0這個目錄可以在Python模塊搜索路徑中找到。 __init__.py包文件 如果選擇使用包導(dǎo)入,那就必須遵循一條約束:包導(dǎo)入語句的路徑的每個目錄內(nèi)部都必須有__init__.py這個文件, 否則導(dǎo)入包會失敗。 dir1和dir2中必須包含__init__.py,容器目錄dir0不需要這類文件。因為本身沒有列在import語句中 __init__.py文件可以包含程序代碼,也可以是空的。 更通常情況下,__init__.py文件扮演了包初始化的掛鉤 替目錄產(chǎn)生模塊命名空間以及用目錄導(dǎo)入實現(xiàn)from *行為的角色。 *包初始化: 首次導(dǎo)入某個目錄時,會自動執(zhí)行該目錄下__init__.py文件中所有程序代碼。 所以這個文件就是放置包內(nèi)文件所需初始化的代碼的場所??梢允褂闷涑跏蓟募?,創(chuàng)建所需的數(shù)據(jù)文件, 連接數(shù)據(jù)庫等。 *模塊命名空間的初始化: *from * 語句的行為: 作為一個高級功能,可以在__init__.py文件中使用__all__列表來定義目錄以form *語句形式導(dǎo)入時,需要 導(dǎo)出什么。__all__列表是指出當(dāng)包(目錄—)名稱使用from *的時候,應(yīng)該導(dǎo)入的子模塊名稱清單。 eg: /usr/local/lib/python2.7/sqlite3/__init__.py from dbapi2 import * /usr/local/lib/python2.7/site-packages/mod_python/__init__.py __all__ = ["apache", "cgihandler", "psp", "publisher", "util", "python22"] version = "3.3.1" 常見的第三方擴(kuò)展都是以包目錄形式發(fā)布給用戶,而不是單純的模塊列表。 這樣就可以通過路徑來導(dǎo)入 7、在模塊中隱藏數(shù)據(jù) 最小化from *的破壞:_X和__all__達(dá)到隱藏變量名的目的 有種特定情況,可以把下劃線放在變量名前(_X),可以防止客戶端使用from * 語句導(dǎo)入模塊名時,把其中的那些變量名賦值出去。這其實是為了把命名空間的破壞最小化而已。下劃線和__all__不是私有聲明,還可以通過其他導(dǎo)入形式修改這類變量名。例如import語句、from module import _X 以外,也可以在模塊頂層把變量名的字符串列表賦值給變量__all__,以達(dá)到類似于_X命名慣例的隱藏效果【__all__是不隱藏的】 mod_python.__all__ 可以看到可以用from *語句復(fù)制那些變量名 _X和__all__ 對比 _X 隱藏了 無法from * __all__ 只顯示,from *只能獲取__all__中指定的,其他隱藏。 python中from *會先尋找模塊內(nèi)的__all__列表,有的話復(fù)制其中的變量名,如果沒有定義的話,from *就會復(fù)制開頭沒有下劃線的所有命令名。 怎么覺得__all__列表里存放的是模塊呢??? 8、混合用法模式:__name__和__main__ 這是一個特殊的與模塊相關(guān)的技巧,可以把文件作為模塊導(dǎo)入,并以獨立式程序的形式運行。每個模塊都有個名為__name__的內(nèi)置屬性。Python會自動設(shè)置該屬性: *如果文件是以頂層程序文件執(zhí)行,在啟動時,__name__就會被設(shè)置為字符串__main__ *如果文件被導(dǎo)入,__name__就會改設(shè)成客戶端所了解模塊名。 結(jié)果就是模塊可以檢測自己的__name__,來確定它是執(zhí)行還是在導(dǎo)入。 定義一個文件test15.py def tester(): print "It's python test!" if __name__=='__main__': tester() 9、修改模塊搜索路徑 可以通過PYTHONPATH以及可能的.pth路徑文件進(jìn)行定制。 Python程序本身是修改sys.path的內(nèi)置列表。sys.path在程序啟動時就進(jìn)行初始化,但那之后也可以隨意對其元素進(jìn)行刪除,附加和重設(shè) >>> import sys >>> sys.path # cd /tmp/ # python >>> sys.path.append('/root')【增加新的路徑】 >>> sys.path ['', '/usr/local/lib/python2.7/site-packages/MySQL_python-1.2.3-py2.7-freebsd-8.2-RELEASE-i386.egg', '/usr/local/lib/python2.7/site-packages/setuptools-0.6c12dev_r88846-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Babel-0.9.6-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Trac-0.12.3-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Genshi-0.6-py2.7.egg', '/usr/local/lib/python2.7/site-packages/IniAdmin-0.2_r10454-py2.7.egg', '/usr/local/lib/python2.7/site-packages/TracAccountManager-0.4dev_r11251-py2.7.egg', '/usr/local/lib/python2.7/site-packages/SvnAuthzAdminPlugin-0.2-py2.7.egg', '/usr/local/lib/python27.zip', '/usr/local/lib/python2.7', '/usr/local/lib/python2.7/plat-freebsd8', '/usr/local/lib/python2.7/lib-tk', '/usr/local/lib/python2.7/lib-old', '/usr/local/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/site-packages', '/root'] 導(dǎo)入/root目錄下test17.py,注意啟動python時在/tmp目錄,所以/root不是程序啟動目錄 >>> import test17 >>> dir(test17) >>> test17.lessthan(3,4) True 10、相對導(dǎo)入語法 from語句現(xiàn)在可以使用點號(.)更傾向于同一個包內(nèi)的模塊(稱為包相對導(dǎo)入),而不是位于模塊導(dǎo)入搜索路徑上其他地方的模塊(所謂的絕對導(dǎo)入) *現(xiàn)在,可以使用點號指出該導(dǎo)入應(yīng)該與其所在包相關(guān)聯(lián):這類導(dǎo)入傾向于導(dǎo)入位于該包內(nèi)的模塊,而不是導(dǎo)入搜索路徑sys.path上其他地方的同名模塊 from .apache CallBack as CB 同一個包內(nèi)導(dǎo)入apache模塊CallBack為CB變量 11、模塊設(shè)計理念 *總是在Python的模塊內(nèi)編寫代碼 *模塊耦合要降到最底:全局變量。模塊應(yīng)該盡可能和其他模塊的全局變量無關(guān)。 *最大化模塊的沾合性:統(tǒng)一目標(biāo) *模塊應(yīng)該少去修改其他模塊的的變量。 模塊是對象:元程序 模塊通過內(nèi)置屬性顯示了他們大多數(shù)的特性。因此,可很容易的編寫程序來管理其他程序。我們通常稱這類管理程序為元程序,因為他們是在其他系統(tǒng)上工作。這也稱為內(nèi)省,因為程序能看見和處理對象的內(nèi)部。內(nèi)省是高級功能,但是它可以做創(chuàng)建程序工具,取得模塊內(nèi)名為name的屬性,方法包括 (1)可以使用結(jié)合點號運算, (2)或者對模塊的屬性字典進(jìn)行索引運算(在內(nèi)置__dict__屬性中顯示)。 (3)Python也在sys.modules字典中導(dǎo)出所有已經(jīng)加載的模塊。 (4)并提供一個內(nèi)置函數(shù)getattrr,讓我們以字符串名來取出屬性。(就好像object.attr,而attr是運行時的字符串) >>> test17.name 'diege' >>> test17.__dict__.keys() >>> test17.__dict__['name'] 'diege >>> test17.__dict__['lessthan'] <function lessthan at 0x28495844> >>> sys.modules 顯示所有加載的模塊 >>> sys.modules['test17'] <module 'test17' from '/root/test17.py'> >>> sys.modules['test17'].name 'diege >>> getattr(test17,'lessthan') <function lessthan at 0x28495bc4> 12、模塊陷阱 1)頂層代碼的語句次序的重要性 *在導(dǎo)入時,模塊文件頂層的程序代碼(不在函數(shù)內(nèi))一旦python運行,就會立刻執(zhí)行。因此,該語句是無法引用文件后面位置賦值的變量名。 *位于函數(shù)主體內(nèi)的代碼知道函數(shù)被調(diào)用后才會運行。 作為一條原則,如果需要把立即執(zhí)行的代碼和def一起混用,就要把def放在文件前面,把頂層代碼放在后面。這樣的話,你的函數(shù)在使用的代碼運行時,可以保證他們都已定義并賦值過了。 2)通過變量名字符串導(dǎo)入模塊的方法 import或from語句內(nèi)的模塊名是”硬編碼“的變量名。 >>> x='string' >>> import x Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named x 這里python會試著導(dǎo)入文件x.py 為了避免發(fā)生這樣的問題,通常的做法就是把import語句構(gòu)造成python代碼的字符串,再傳給exec語句執(zhí)行: >>> modname='string' >>> exec "import "+modname exec語句以及和他類似eval會編譯一段字符串代碼,將其傳給Python解析器來執(zhí)行。 3)from復(fù)制變量名,而不是連接 from語句其實是在導(dǎo)入者的作用域內(nèi)對變量名的賦值語句,也就是變量名拷貝運算,而不是變量名的別名機(jī)制。它的實現(xiàn)和python所有賦值運算都一樣,微妙之處在于,共享對象的代碼存在于不同的文件中。然后,我們使用import獲得了整個模塊,然后賦值某個點號運算的變量名,就會修改導(dǎo)入的模塊中的變量名。點號運算把python定向到了模塊對象,而不是賦值模塊中對象。 4)from*會讓變量語義模糊 5)reload不會影響from導(dǎo)入 6)不要使用reload、from以及交互模式測試 reload中引用模塊得通過import至少將其加載一次: 不要from導(dǎo)入之后reload 7) reload使用沒有傳遞性 當(dāng)重載一個模塊時,Python只會重載那個模塊的文件,不會自動重載該文件重載嘶碰巧還要導(dǎo)入的模塊。 8)遞歸形式的from import無法工作 不要在遞歸導(dǎo)入中使用 from。 |
|