国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

詳談Python高階函數(shù)與函數(shù)裝飾器(推薦)

2020-01-04 16:33:03
字體:
供稿:網(wǎng)友

一、上節(jié)回顧

Python2與Python3字符編碼問題,不管你是初學(xué)者還是已經(jīng)對Python的項(xiàng)目了如指掌了,都會犯一些編碼上面的錯(cuò)誤。我在這里簡單歸納Python3和Python2各自的區(qū)別。

首先是Python3-->代碼文件都是用utf-8來解釋的。將代碼和文件讀到內(nèi)存中就變成了Unicode,這也就是為什么Python只有encode沒有decode了,因?yàn)閮?nèi)存中都將字符編碼變成了Unicode,而Unicode是萬國碼,可以“翻譯”所以格式編碼的格式。Python3中str和bytes是兩種格式,bytes可以當(dāng)做二進(jìn)制的表現(xiàn)形式。

Python2使用系統(tǒng)默認(rèn)的字符編碼解釋代碼,所以要用utf-8解釋代碼,就必須在頭部申明;并且Python2中有解碼和編碼,但是解碼動(dòng)作是必須的而編碼動(dòng)作可以忽略,因?yàn)镻ython代碼加載到內(nèi)存中就是Unicode,這一點(diǎn)和python3一樣;Python2中還需要注意的就是str和bytes是一個(gè)意思。Python2 里面的str就是Python3中的bytes格式,而Python3中的str其實(shí)就是Unicode.

函數(shù)基礎(chǔ)(這里我就是用遞歸函數(shù)中的二分查找)

為什么使用函數(shù):將將程序進(jìn)行模塊設(shè)計(jì)

定義函數(shù)有三種形式:

- 無參函數(shù)

- 有參函數(shù)

- 空函數(shù)

PS:如果函數(shù)有多個(gè)返回值,那么返回的來的數(shù)據(jù)格式是元組

- 如何在函數(shù)傳入?yún)?shù)時(shí)限定參數(shù)數(shù)據(jù)格式。

def leon(x:int,y:int)->int:

pass

其中這里指定了x,y都必須是int類型 " -> "的意思是函數(shù)返回值也必須是int類型

print(yan.__annotations__):顯示形參的限定數(shù)據(jù)格式以及返回值的格式

a = [1,2,3,4,5,7,9,10,11,12,14,15,16,17,19,21] #形參中的numdef calc(num,find_num): print(num) mid = int(len(num) / 2)   #中間數(shù)的下標(biāo) if mid == 0: #遞歸函數(shù)非常重要的判斷條件 if num[mid] == find_num:  print("find it %s"%find_num) else:  print("cannt find num") if num[mid] == find_num: #直接找到不用遞歸,結(jié)束函數(shù) print("find_num %s"%find_num) elif num[mid] > find_num: #find_num應(yīng)該在左邊,向下遞歸 calc(num[0:mid],find_num) elif num[mid] < find_num: #find_num應(yīng)該在右邊,向下遞歸 calc(num[mid+1:],find_num)calc(a,12)

匿名函數(shù)

c = lambda x:x+1 #x就是形參,c就是這個(gè)匿名函數(shù)的對象print(c(22)) 

高階函數(shù)-特性

1. 把一個(gè)函數(shù)的內(nèi)存地址傳給另外一個(gè)函數(shù),當(dāng)做參數(shù)

2.一個(gè)函數(shù)把另外一個(gè)函數(shù)的當(dāng)做返回值返回

def calc(a,b,c):print(c(a) + c(b))calc(-5,10,abs) #引用上一節(jié)的實(shí)例,將-5和10絕對值相加

 

二、高階函數(shù)(補(bǔ)充)

函數(shù)是第一類對象

函數(shù)可以被賦值

可以被當(dāng)做參數(shù)

可以當(dāng)做返回值

可以作為容器類型的元素

#函數(shù)可以被賦值def leon(): print("in the leon")l = leonl()#函數(shù)可以被當(dāng)做參數(shù)def yan(x): #這里x形參,其實(shí)就是我們調(diào)用實(shí)參的函數(shù)名 x() #運(yùn)行函數(shù)y = yan(leon)#函數(shù)當(dāng)做返回值def jian(x): 和上面一樣這這也必須傳入一個(gè)函數(shù) return xj = jian(leon) #這里需要注意一點(diǎn)就是這里的意思是運(yùn)行jian這個(gè)函數(shù)而這個(gè)函數(shù)返回的是x 也就是leon這個(gè)函數(shù)的內(nèi)存地址,也就是說這時(shí)候leon這個(gè)函數(shù)并沒有被執(zhí)行j() #運(yùn)行 leon函數(shù)#可以做為容器類型的元素leon_dict = {"leon":leon}leon_dict["leon"]() #這樣也可以運(yùn)行l(wèi)eon這個(gè)函數(shù)

三、閉包函數(shù)

1.什么是閉包?我來看一下,比較官網(wǎng)的概念(這不是我在官網(wǎng)上面找的,不過沒有關(guān)系,反正你們也看不懂):

閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。所以,閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。

懵逼了?不存在的。下面我用簡潔的說一下,但是有一點(diǎn)很重要,閉包是裝飾器中的重點(diǎn),如果沒有把閉包正真理解,那么學(xué)完裝飾器之后會很快忘記。我們通過一個(gè)列子來說明下

import requests #首先導(dǎo)入一個(gè)模塊,這個(gè)可以不用記def get(url): #定義一個(gè)get函數(shù)里面需要傳一個(gè)url的位置參數(shù) def wapper(): #在定義一個(gè)wapper函數(shù) res = requests.get(url) #這一步就是打開一個(gè)網(wǎng)頁 return res.text #將網(wǎng)頁以文字的形式返回 return wapper #返回最里層的wapper函數(shù)g = get("http://www.baidu.com") #調(diào)用:首先因?yàn)樽饔糜虻脑颍覀儫o法訪問到里層的wapper函數(shù),所以我們直接調(diào)用get函數(shù)這里返回了一個(gè)wapper函數(shù)print(g()) # 然后我在調(diào)用g(get函數(shù))的對象,這樣是不是就訪問到里層的wapper函數(shù)呢

python,高階函數(shù),函數(shù)裝飾器

PS:這里我們可以把函數(shù)當(dāng)做一個(gè)特殊的變量,當(dāng)代碼從上向下執(zhí)行的時(shí)候,如果函數(shù)不被調(diào)用話,函數(shù)內(nèi)的代碼是不會被執(zhí)行的。就拿上面的上面的舉例,當(dāng)我們執(zhí)行g(shù)et函數(shù)的時(shí)候,這時(shí)候會返回一個(gè)wapper函數(shù)的內(nèi)存地址,但是這個(gè)時(shí)候wapper函數(shù)并沒有被執(zhí)行也就是說g()這時(shí)候返回的狀態(tài)其實(shí)就是wapper,這是我們只需要將g運(yùn)行,就等于運(yùn)行了wapper內(nèi)的代碼。

四、函數(shù)的嵌套調(diào)用

嵌套調(diào)用其實(shí)很好理解,就是在一個(gè)函數(shù)中調(diào)用另一個(gè)函數(shù)的結(jié)果,也就是return的東西,同樣的我們看一段非常簡單的代碼來看一下。

#嵌套調(diào)用,在一個(gè)函數(shù)中調(diào)用另一個(gè)函數(shù)的功能#calc這個(gè)函數(shù)就是在對比兩個(gè)數(shù)字的大小def calc2(x,y): if x >y : return x else: return y#我靠老板非常變態(tài),然你直接計(jì)算四個(gè)數(shù)字的大小,擦。def calc4(a,b,c,d): res1 = calc2(a,b) #res1的值,這里不就是calc2這個(gè)函數(shù)比較時(shí)最大的哪一個(gè)嗎。 res2 = calc2(res1,c) res3 = calc2(res2,d) return res3

通過上面的代碼我們做一記憶。什么時(shí)候會用到嵌套調(diào)用呢?很顯然,就是我們這個(gè)函數(shù)(calc4)需要另外一個(gè)函數(shù)的實(shí)行結(jié)果(return的y或者x)。

五、裝飾器(高級的閉包函數(shù))

就拿下面的這段代碼來說。如何在不改源代碼的情況下實(shí)現(xiàn)計(jì)算代碼的運(yùn)行時(shí)間

def geturl(url): response = requests.get(url) print(response.status_code)geturl(http://www.baidu.com)
def timer(func): def wapper(url): start_time = time.time() func(url) stop_time = time.time() so_time_is = stop_time - start_time print("運(yùn)行時(shí)間%s"%so_time_is) return wapper@timerdef geturl(url): response = requests.get(url) print(response.status_code)python = geturl(http://www.baidu.com)

圖解代碼

python,高階函數(shù),函數(shù)裝飾器

裝飾器必備:

@timer就是裝飾器,意思是裝飾它下面的函數(shù),而裝飾器和被裝飾的都是一個(gè)函數(shù)。

timer(裝飾器函數(shù)),首先它會有一個(gè)位置參數(shù)(func)名字隨意,但是必須并且只能是一個(gè)位置參數(shù)

func參數(shù)就是被裝飾的geturl這個(gè)函數(shù)

為什么func是geturl這個(gè)函數(shù)呢-->上面寫了一個(gè)裝飾器功能:geturl=timer(geturl),我們看到這里的timer中傳入的其實(shí)就是func函數(shù)所以func = geturl(被裝飾的函數(shù))

分析geturl=timer(geturl),首先我們可以得知timer這是一個(gè)閉包函數(shù),當(dāng)我們執(zhí)行這個(gè)閉包函數(shù),會把里層的函數(shù)(wapper)返回,也就是說timer(geturl)其實(shí)就是返回的wapper,所以就可以這樣理解了geturl==wapper,所以當(dāng)我們運(yùn)行g(shù)eturl的時(shí)候就相當(dāng)于在執(zhí)行wapper()這樣的一個(gè)操作;如果這里實(shí)在記不住,就這樣。咱上面不是有一個(gè)閉包函數(shù)嗎?你就把geturl=timer(geturl)中的geturl(執(zhí)行函數(shù)的返回結(jié)果)當(dāng)做上面g(函數(shù)調(diào)用的返回結(jié)果),然后在分別再執(zhí)行了下"g"或者"geturl”這個(gè)對象。

如果被裝飾者有位置參數(shù)的話,我們需要在wapper函數(shù)中加上對應(yīng)的位置參數(shù)用來接收,如果長度是不固定的話還可以用*args和**kwargs

六、有參裝飾器

聽著名字顧名思義,就是在裝飾器中還有位置參數(shù)。

#一個(gè)low得不能再low得驗(yàn)證腳本,如果是顯示環(huán)境中所有數(shù)據(jù)必須是由數(shù)據(jù)庫或者一個(gè)靜態(tài)文件提供,并且登錄成功時(shí),需要保存用戶的一個(gè)狀態(tài)def auth(auth_type): #有參裝飾器名稱 def auth_deco(func): #定義第二層函數(shù)名稱 def wrapper(*args,**kwargs): #最里層函數(shù),主要實(shí)現(xiàn)認(rèn)證功能  if auth_type == "file":  username = input("username>>:").strip()  password = input("username>>").strip()  if username == "leon" and password == "loveleon":   res = func(*args,**kwargs)   return res  elif auth_type == "mysql_auth":  print("mysql_auth...")  return func(*args,**kwargs) return wrapper #第二層返回的是wrapper函數(shù),其實(shí)就是home return auth_deco #第一層返回的結(jié)果等于第二層函數(shù)的名稱@auth('file')def home(): print("welcome")home() #執(zhí)行home-->wrapper

有參函數(shù)必備知識:

套路,通過上面無參裝飾器,我們得出了geturl=timer(geturl)這個(gè)等式。回到有參裝飾器,我們又會有什么樣子的等式呢?首先@auth("file")是一個(gè)裝飾器也就是一個(gè)函數(shù),所以我們定義了一個(gè)auth(auth_type)這個(gè)函數(shù),而這個(gè)函數(shù)返回的是什么呢?沒有錯(cuò)就是第二層函數(shù);到了這里我們就會發(fā)現(xiàn)@auth("file")其實(shí)就是@auth_deco,現(xiàn)在我們知道了現(xiàn)在裝飾器其實(shí)就是auth_deco,那剩下的還不知道怎么寫嗎?

整理公式,auth('file')-----------(return)> auth_deco----->@auth_deco ->home=auth_deco(home)

如果記不住?如果實(shí)在是記不住,其實(shí)就可以這樣理解,有參裝飾器無非就是在無參裝飾器上面加了一層(三層),然后在第一層返回了第二層的函數(shù),而到了第二層就和我們普通用的裝飾器是一毛一樣了

七、模塊導(dǎo)入

import ,創(chuàng)建一個(gè)leonyan.py的模塊文件,等待被導(dǎo)入

a = 10b = 20c = 30def read1(): print("in the read1")def read2(): print("in the read2")

導(dǎo)入leonyan.py文件(調(diào)用模塊文件和模塊文件在同一目錄下)

import leonyan #Python IDE這行會爆紅,但是不用管leonyan.read1() #執(zhí)行l(wèi)eonyan這個(gè)包中的read1函數(shù)leonyan.read2() #執(zhí)行l(wèi)eonyan這個(gè)包中read2函數(shù)print(leonyan.a + leonyan.b + leonyan.c ) #輸出60

總結(jié):在Python中包的導(dǎo)入(import ***)會干三個(gè)事情:1:創(chuàng)建新的作用域;2:執(zhí)行該作用域的頂級代碼,比如你導(dǎo)入的那個(gè)包中有print執(zhí)行后就會直接在屏幕中輸出print的內(nèi)容;3:得到一個(gè)模塊名,綁定到該模塊內(nèi)的代碼

在模塊導(dǎo)入的時(shí)候給模塊起別名

import leonyan as lyimport pandas as pd #這是一個(gè)第三方模塊,以后的博客中會寫到,這是一個(gè)用于做統(tǒng)計(jì)的

 

給模塊起別名還是挺多的,在有些模塊的官方文檔中,還是比較推薦這種方法的,比如pandas的官方文檔中就是起了一個(gè)pd別名,總之a(chǎn)s就是一個(gè)模塊起別名

from *** import ***from leonyan import read1 #引入直接調(diào)用read1() 

 

如果在調(diào)用模塊的函數(shù)作用域中有相同的同名的,會將調(diào)用過來的覆蓋。

在form ** import ** 中控制需要引用的變量(函數(shù)其實(shí)在未被執(zhí)行的時(shí)候也是一個(gè)存放在內(nèi)存中的變量)

from leonyan import read1,read2 在同一行中可以引用多個(gè),只需要用逗號隔開就行了print(read1)print(read2)#這里打印的就是read1和read2的內(nèi)存地址#需求我現(xiàn)在只需要導(dǎo)入read2這時(shí)候我們就可以在leonyan這個(gè)函數(shù)中加上這么一行:__all__ = ["read2"] #這里的意思就是別的文件調(diào)用為的時(shí)候用from ** import ** 只能拿到read2 這個(gè)函數(shù)的內(nèi)存地址,也就是只有read2可以被調(diào)用

把模塊當(dāng)做一個(gè)腳本執(zhí)行

我們可以通過模塊的全局變量__name__來查看模塊名:

當(dāng)做腳本運(yùn)行:

__name__ 等于'__main__'

作用:用來控制.py文件在不同的應(yīng)用場景下執(zhí)行不同的邏輯

if __name__ == '__main__':

#fib.pydef fib(n): # write Fibonacci series up to n a, b = 0, 1 while b < n: print(b, end=' ') a, b = b, a+b print()def fib2(n): # return Fibonacci series up to n result = [] a, b = 0, 1 while b < n: result.append(b) a, b = b, a+b return resultif __name__ == "__main__": import sys fib(int(sys.argv[1]))

代碼執(zhí)行 Python flb.py 100

只需要簡單了解的Python模塊導(dǎo)入搜索路徑

內(nèi)建(build-in)  --> sys.path(sys.path是一個(gè)列表,而且第一個(gè)位置就是當(dāng)前文件夾)

模塊導(dǎo)入的重點(diǎn)-->包的導(dǎo)入

在實(shí)際的開發(fā)環(huán)境中,你不可能一個(gè)文件的代碼寫到底,當(dāng)然你也有可能會引用同文件夾中的其他模塊,但是你有沒有想過這一個(gè)項(xiàng)目不可能是你一個(gè)寫的,都是很多人協(xié)作開發(fā)。這樣就存在這樣的一個(gè)問題了;不同的人不可能用一臺電腦,也不可能在一個(gè)文件夾下面寫寫功能。他們也有自己的代碼文件夾,然后大家把功能通過接口的方式,提供調(diào)用。這時(shí)候就面臨這不同文件夾的調(diào)用問題。而這種問題也需要通過from ** import ** 調(diào)用。

python,高階函數(shù),函數(shù)裝飾器

上圖中我運(yùn)行“模塊導(dǎo)入.py”這個(gè)文件夾,首先我from Pythonscript.leonyan.command import config,因?yàn)槲覀兊眠\(yùn)行腳本和需要導(dǎo)入的包都在Pythonscript的目錄下面所以我直接通過絕對路徑導(dǎo)入,然后一層一層“.”下去,知道最后import了這個(gè)config文件,這里我們需要注意一點(diǎn):當(dāng)腳本在最外層運(yùn)行的時(shí)候sys.path 列表中的第一個(gè)參數(shù)就是運(yùn)行腳本的目錄,這是什么意思,這代表著你在別的包中有調(diào)用了其他的東西比如說我的config.py是調(diào)用了bing.py文件,這時(shí)候就必須寫絕對路徑,因?yàn)檫@在sys.path文件夾中已經(jīng)找不到了,也就是導(dǎo)入不進(jìn)來。

總結(jié):包的導(dǎo)入其實(shí)都是很簡單的,你需要記住一點(diǎn):當(dāng)你導(dǎo)入Python內(nèi)建或者下載的第三方模塊直接用import 導(dǎo)入,如果是自己寫的就用from ** import ** 使用絕對目錄導(dǎo)入就行了,也就是從調(diào)用腳本的上級目錄開始導(dǎo)入。這樣可以保證不會報(bào)模塊導(dǎo)入的錯(cuò)誤了。

以上這篇詳談Python高階函數(shù)與函數(shù)裝飾器(推薦)就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持VEVB武林網(wǎng)。


注:相關(guān)教程知識閱讀請移步到python教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 象山县| 黄平县| 溧阳市| 乐清市| 九龙县| 东海县| 盐源县| 长汀县| 疏附县| 汤阴县| 柏乡县| 方正县| 罗源县| 灵山县| 双流县| 吉隆县| 赫章县| 稻城县| 江都市| 容城县| 景泰县| 措美县| 五原县| 余庆县| 丰原市| 烟台市| 临泽县| 麻阳| 忻城县| 屯留县| 曲水县| 霍林郭勒市| 平凉市| 大竹县| 黄石市| 台中县| 塔河县| 松潘县| 扎囊县| 攀枝花市| 济宁市|