函數(shù)是組織好,可重復使用的,用來實現(xiàn)相關(guān)功能的代碼段
創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,提供成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè),網(wǎng)頁設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);可快速的進行網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,是專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
函數(shù)提高了代碼的重復利用率和應用的模塊性。
除Python自帶的函數(shù)之外,也可以自己創(chuàng)建函數(shù),叫做自定義函數(shù)
語法:
函數(shù)代碼塊以 def 開頭
參數(shù)為 輸入值 ,放于函數(shù)名后口號里面,
函數(shù)內(nèi)容以冒號:開始,函數(shù)體縮進, return 返回 輸出值
函數(shù)調(diào)用使用關(guān)鍵字參數(shù)來確定傳入的參數(shù)值,此時,如果多個函數(shù)則不需要按照指定順序。
在定義函數(shù)時,指定參數(shù)默認值。調(diào)用時如果不傳入?yún)?shù),則使用默認值
不定長部分如果沒有指定參數(shù),傳入是一個空元組
加了 兩個星號 ** 的參數(shù)會以字典的形式導入
/ 用來指明函數(shù)形參必須使用指定位置參數(shù),不能使用關(guān)鍵字參數(shù)的形式。
3.8版本之后的才能使用
不使用 def 定義函數(shù),沒有函數(shù)名
lamdba主體時一個表達式,而不是代碼塊,函數(shù)體比def簡單很多
定義在函數(shù)內(nèi)部的為局部變量,僅能在函數(shù)內(nèi)部使用
定義在函數(shù)外部的為全局變量,可在全局使用
模塊是將包含所有定義的函數(shù)和變量的文件,一般將同類功能的函數(shù)組和在一起稱為模塊。
模塊需要導入后,在調(diào)用相應函數(shù)進行使用
模塊導入的方法:
從模塊中導入一個指定的部分
把一個模塊的所有內(nèi)容全都導入
eval類似exec,是使用python編譯器運行表達式和語句
兩者區(qū)別在于:eval是編譯表達式并返回值(如: eval("'hello'*2") 結(jié)果是 hellohello)
exec則是運行一部分代碼,并且不像eval那樣返回結(jié)果,exec的返回值永遠是None,且exec可運行多行代碼(如: exec("l=[1,2,3]\nfor i in l:\nprint(i,end=',')") 輸出為"1,2,3," 并且沒有返回值)
本文目的是由淺入深地介紹python裝飾器原理
裝飾器(Decorators)是 Python 的一個重要部分
其功能是, 在不修改原函數(shù)(類)定義代碼的情況下,增加新的功能
為了理解和實現(xiàn)裝飾器,我們先引入2個核心操作:
在這個例子中,函數(shù)hi的形參name,默認為'world'
在函數(shù)內(nèi)部,又定義了另一個函數(shù) howdoyoudo,定義這個函數(shù)時,將形參name作為新函數(shù)的形參name2的默認值。
因此,在函數(shù)內(nèi)部調(diào)用howdoyoudo()時,將以調(diào)用hi時的實參為默認值,但也可以給howdoyoudo輸入其他參數(shù)。
上面的例子運行后輸出結(jié)果為:
這里新定義的howdoyoudo可以稱作一個“閉包”。不少關(guān)于裝飾器的blog都提到了這個概念,但其實沒必要給它取一個多專業(yè)的名字。我們知道閉包是 函數(shù)內(nèi)的函數(shù) 就可以了
當我們進行 def 的時候,我們在做什么?
這時,hi函數(shù),打印一個字符串,同時返回一個字符串。
但hi函數(shù)本身也是一個對象,一個可以執(zhí)行的對象。執(zhí)行的方式是hi()。
這里hi和hi()有本質(zhì)區(qū)別,
hi 代表了這個函數(shù)對象本身
hi() 則是運行了函數(shù),得到函數(shù)的返回值。
作為對比,可以想象以下代碼
此時也是b存在,可以正常使用。
我們定義2個函數(shù),分別實現(xiàn)自加1, 自乘2,
再定義一個函數(shù)double_exec,內(nèi)容是將某個函數(shù)調(diào)用2次
在調(diào)用double_exec時,可以將函數(shù)作為輸入傳進來
輸出結(jié)果就是
7
27
同樣,也可以將函數(shù)作為輸出
輸出結(jié)果為
6
10
有了以上兩個核心操作,我們可以嘗試構(gòu)造裝飾器了。
裝飾器的目的: 在不修改原函數(shù)(類)定義代碼的情況下,增加新的功能
試想一下,現(xiàn)在有一個原函數(shù)
在不修改原函數(shù)定義代碼的情況下,如果想進行函數(shù)內(nèi)容的添加,可以將這個函數(shù)作為一個整體,添加到這樣的包裹中:
我們定義了一個my_decorator函數(shù),這個函數(shù)進行了一種操作:
對傳入的f,添加操作(運行前后增加打印),并把添加操作后的內(nèi)容連同運行原函數(shù)的內(nèi)容,一起傳出
這個my_decorator,定義了一種增加前后打印內(nèi)容的行為
調(diào)用my_decorator時,對這個行為進行了操作。
因此,new_function是一個在original_function上增加了前后打印行為的新函數(shù)
這個過程被可以被稱作裝飾。
這里已經(jīng)可以發(fā)現(xiàn),裝飾器本身對于被裝飾的函數(shù)是什么,是不需要考慮的。裝飾器本身只定義了一種裝飾行為,這個行為是通過裝飾器內(nèi)部的閉包函數(shù)()進行定義的。
運行裝飾前后的函數(shù),可以清晰看到裝飾的效果
我們復現(xiàn)一下實際要用裝飾器的情況,我們往往有一種裝飾器,想應用于很多個函數(shù),比如
此時,如果我們想給3個print函數(shù)都加上裝飾器,需要這么做
實際調(diào)用的時候,就需要調(diào)用添加裝飾器的函數(shù)名了
當然,也可以賦值給原函數(shù)名
這樣至少不需要管理一系列裝飾前后的函數(shù)。
同時,在不需要進行裝飾的時候,需要把
全部刪掉。
事實上,這樣并不方便,尤其對于更復雜的裝飾器來說
為此,python提供了一種簡寫方式
這個定義print1函數(shù)前的@my_decorator,相當于在定義完print1后,自動直接運行了
不論采用@my_decorator放在新函數(shù)前,還是顯示地重寫print1 = my_decorator(print1),都會存在一個問題:
裝飾后的函數(shù),名字改變了(其實不止名字,一系列的索引都改變了)
輸出結(jié)果為:
這個現(xiàn)象的原因是,裝飾行為本身,是通過構(gòu)造了一個新的函數(shù)(例子中是wrap_func函數(shù))來實現(xiàn)裝飾這個行為的,然后把這個修改后的函數(shù)賦給了原函數(shù)名。
這樣,會導致我們預期的被裝飾函數(shù)的一些系統(tǒng)變量(比如__name__)發(fā)生了變化。
對此,python提供了解決方案:
經(jīng)過這個行為后,被裝飾函數(shù)的系統(tǒng)變量問題被解決了
輸出結(jié)果為
剛才的例子都比較簡單,被裝飾的函數(shù)是沒有參數(shù)的。如果被裝飾的函數(shù)有參數(shù),只需要在定義裝飾行為時(事實上,這個才更通用),增加(*args, **kwargs)描述即可
之前的描述中可以感受到,對于例子中的裝飾行為(前后加打印),函數(shù)被裝飾后,本質(zhì)上是調(diào)用了新的裝飾函數(shù)wrap_func。
因此,如果原函數(shù)需要有輸入?yún)?shù)傳遞,只需要在wrap_func(或其他任意名字的裝飾函數(shù))定義時,也增加參數(shù)輸入(*args, **kwargs),并將這些參數(shù),原封不動地傳給待裝飾函數(shù)f。
這種定義裝飾行為的方式更具有普遍性,忘記之前的定義方式吧
我們試一下
輸出
這里需要注意的是,如果按照以下的方式定義裝飾器
那么以下語句將不會執(zhí)行
因為裝飾后實際的函數(shù)wrap_func(雖然名字被改成了原函數(shù),系統(tǒng)參數(shù)也改成了原函數(shù)),運行到return f(*args, **kwargs) 的時候已經(jīng)結(jié)束了
因為裝飾器my_decorator本身也是可以輸入的,因此,只需要在定義裝飾器時,增加參數(shù),并在后續(xù)函數(shù)中使用就可以了,比如
此時裝飾器已經(jīng)可以有輸入?yún)?shù)了
輸出
你可能發(fā)現(xiàn),為什么不用簡寫版的方法了
因為以上代碼會報錯??!
究其原因,雖然
等價于
但是,
并不等價于
這本身和@語法有關(guān),使用@my_decorator時,是系統(tǒng)在應用一個以單個函數(shù)作為參數(shù)的閉包函數(shù)。即,@是不能帶參數(shù)的。
但是你應該發(fā)現(xiàn)了,之前的@wraps(f)不是帶參數(shù)了嗎?請仔細觀察以下代碼
通過一層嵌套,my_decorator_with_parma本質(zhì)上是返回了一個參數(shù)僅為一個函數(shù)的函數(shù)(my_decorator),但因為my_decorator對my_decorator_with_parma來說是一個閉包,my_decorator_with_parma是可以帶參數(shù)的。(這句話真繞)
通過以上的定義,我們再來看
可以這么理解,my_decorator_with_parma(msg='yusheng')的結(jié)果是原來的my_decorator函數(shù),同時,因為my_decorator_with_parma可以傳參,參數(shù)實際上是參與了my_decorator的(因為my_decorator對my_decorator_with_parma是閉包), my_decorator_with_parma(msg='yusheng') 全等于 一個有參數(shù)參加的my_decorator
因此,以上代碼等價于有參數(shù)msg傳遞的
比較繞,需要理解一下,或者干脆強記這種范式:
以上范式包含函數(shù)的輸入輸出、裝飾器的輸入,可以應對大部分情況了。
實驗一下:
輸出
以上是一個log裝飾器,利用datetime統(tǒng)計了函數(shù)的耗時,
并且,裝飾器可以進行輸出文件操作,如果給出了文件路徑,則輸出文件,否則就打印。
利用這個裝飾器,可以靈活地進行耗時統(tǒng)計
不設(shè)置輸出文件地址,則打印。運行結(jié)果為:
也可以輸出到文件
輸出結(jié)果為
同時在當前目錄生成了一個test.log 文件,內(nèi)容為:
以上的裝飾器都是以函數(shù)形式出現(xiàn)的,但我們可以稍做改寫,將裝飾器以類的形式實現(xiàn)。
這個裝飾器類Log 上個例子里的裝飾器函數(shù)log功能是一樣的,同時,這個裝飾器類還可以作為基類被其他繼承,進一步增加功能。
原文
dict對象是Python中一個原始的數(shù)據(jù)類型,按照鍵值對的方式存儲,中文名為字典,其通過鍵名查找對應的值有很高的效率,時間復雜度在常數(shù)級別O(1)。Python dict的底層是依靠哈希表(Hash Table)進行實現(xiàn)的,使用開放地址法解決沖突。所以其查找的時間復雜度會是O(1),why?
哈希表是key-value類型的數(shù)據(jù)結(jié)構(gòu),通過關(guān)鍵碼值直接進行訪問。通過散列函數(shù)進行鍵和數(shù)組的下標映射從而決定該鍵值應該放在哪個位置,哈希表可以理解為一個鍵值需要按一定規(guī)則存放的數(shù)組,而哈希函數(shù)就是這個規(guī)則。
算法中時間和空間是不能兼得的,哈希表就是一種用合理的時間消耗去減少大量空間消耗的操作,這取決于具體的功能要求。
創(chuàng)建一個數(shù)組,數(shù)組下標是索引號,數(shù)組中的值是要獲得的數(shù)據(jù),這樣只需要O(1)的時間復雜度就可以完成操作,但是擴展性不強,有以下兩個方面的考慮:
-1- 新添加的元素超出數(shù)組索引范圍,這就需要重新申請數(shù)組進行遷移操作。
-2- 假設(shè)一種極端的情況:只存在兩個元素,索引號分別是1和100000000001,按照先前的設(shè)計思路,會浪費很大的存儲空間。
會不會存在一個方法,為已有的索引創(chuàng)建新的索引,通過壓縮位數(shù),讓新索引可以和原有的大范圍的稀疏索引進行一一對應,新索引所需要的存儲空間要大大減小,這就是哈希思想。
上面的例子中哈希函數(shù)的設(shè)計很隨意,但是從這個例子中我們也可以得到信息:
哈希函數(shù)就是一個映射,因此哈希函數(shù)的設(shè)定很靈活,只要使得任何關(guān)鍵字由此所得的哈希函數(shù)值都落在表長允許的范圍之內(nèi)即可;
因為新的索引對舊的索引進行了空間上的壓縮,所以不可能所有的輸入都只對應唯一一個輸出,也就是哈希函數(shù)式有可能發(fā)生沖突的,哈希函數(shù)不可能做成一對一的映射關(guān)系,其本質(zhì)是一個多對一的映射。
直接定址法:很容易理解,key=Value+C; 這個“C”是常量。Value+C其實就是一個簡單的哈希函數(shù)。
除法取余法: 很容易理解, key=value%C;解釋同上。
數(shù)字分析法:這種蠻有意思,比如有一組value1=112233,value2=112633,value3=119033,針對這樣的數(shù)我們分析數(shù)中間兩個數(shù)比較波動,其他數(shù)不變。那么我們?nèi)ey的值就可以是key1=22,key2=26,key3=90。
平方取中法。此處忽略,見名識意。
折疊法:這種蠻有意思,比如value=135790,要求key是2位數(shù)的散列值。那么我們將value變?yōu)?3+57+90=160,然后去掉高位“1”,此時key=60,哈哈,這就是他們的哈希關(guān)系,這樣做的目的就是key與每一位value都相關(guān),來做到“散列地址”盡可能分散的目地。
當兩個不同的數(shù)據(jù)元素的哈希值相同時,就會發(fā)生沖突。解決沖突常用的手法有2種:
開放地址法:
如果兩個數(shù)據(jù)元素的哈希值相同,則在哈希表中為后插入的數(shù)據(jù)元素另外選擇一個表項。當程序查找哈希表時,如果沒有在第一個對應的哈希表項中找到符合查找要求的數(shù)據(jù)元素,程序就會繼續(xù)往后查找,直到找到一個符合查找要求的數(shù)據(jù)元素,或者遇到一個空的表項。
鏈接法:
將哈希值相同的數(shù)據(jù)元素存放在一個鏈表中,在查找哈希表的過程中,當查找到這個鏈表時,必須采用線性查找方法。
python的dict采用了哈希表,最低能在 O(1)時間內(nèi)完成搜索,在發(fā)生哈希沖突的時候采用的是開放尋址法。java的HashMap也是采用了哈希表實現(xiàn),但是在發(fā)生哈希沖突的時候采用的是鏈接法。
文章題目:包含python函數(shù)實現(xiàn)原理的詞條
網(wǎng)頁網(wǎng)址:http://chinadenli.net/article16/hdodgg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、靜態(tài)網(wǎng)站、關(guān)鍵詞優(yōu)化、網(wǎng)站制作、網(wǎng)站收錄、網(wǎng)站策劃
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)