Python - 函式 - 閉包、裝飾器、遞迴
阿新 • • 發佈:2018-11-22
目錄
一、閉包 - 內部函式包含,對外層作用域而非全域性作用域的引用
2-5 @wraps 對裝飾器內層函式的使用 -- 讓使用裝飾器的函式 呼叫__name__,__doc__的時候可以指向原有函式屬性
一、閉包 - 內部函式包含,對外層作用域而非全域性作用域的引用
def counter(): n = 0 def incr(): nonlocal n # 修改巢狀作用域(enclosing 作用域,外層非全域性作用域)中的變數 x = n n += 1 return x return incr c = counter() print(c()) # 第一次 n=0 x=0 n++ print(c())# 2 n=1 x=1 n++ print(c()) print(c.__closure__[0].cell_contents) # 檢視閉包的元素
1-1 閉包的意義 - 優先使用自己外層包裹的作用域
''' 閉包的意義: 返回的函式物件,不僅僅是一個函式物件,在該函式外還包裹了一層作用域, 這使得,該函式無論在何處呼叫,優先使用自己外層包裹的作用域 ''' # 應用領域:延遲計算(原來我們是傳參,現在我們是包起來) from urllib.request import urlopen def index(url): def get(): return urlopen(url).read() return get baidu = index('http://www.baidu.com') print(baidu().decode('utf-8'))
二、裝飾器 - 閉包的一種應用方式
概念:裝飾器他人的器具,本身可以是任意可呼叫物件,被裝飾者也可以是任意可呼叫物件。
即:裝飾器是在不修改被裝飾物件原始碼和呼叫方式的前提下,為被裝飾物件新增新功能的工具原則:
- 不修改被裝飾物件的原始碼
- 不修改被裝飾物件的呼叫方式
目標: 在遵循原則1和2的前提下,為被裝飾物件新增上新功能
執行原理:python直譯器,一旦執行到@裝飾器的名字,就會呼叫裝飾器,然後將被裝飾函式的記憶體地址,當做引數傳給裝飾器,最後將裝飾器呼叫的結果賦值給原函式名
函式上新增多個裝飾器的執行順序:解釋呼叫@語法的時候是自下而上執行(即從越靠近函式的執行器先先執行) 執行裝飾器內部函式的wrapper的時候是自上而下
即,func = @outter_最外層(@outter_中間(@outter_內層))
2-1 不使用裝飾器實現裝飾器的功能 - 使用閉包
import time # 原函式index(),無參 def index(): print('welcome to index page') time.sleep(3) # 原函式home(name) 有參 def home(name): print('welcome to index page') time.sleep(2) return 123 # 閉包函式,用來接收index的地址並新增新功能之後返回 def outter(func): def wrapper(*args, **kwargs): # 讓傳入的引數 原封不動,則可以解決原函式對引數(有參無參)的要求 start = time.time() res = func(*args, **kwargs) # 執行形參函式,並且將func的返回值賦給res stop = time.time() print('run time is %s' % (stop - start)) return res # 返回傳入函式的返回值 return wrapper index = outter(index) # 將index地址傳入putter,返回wrapper地址 # 當前重新複製的index儲存著wrapper的地址 index() home = outter(home) # home=outter(最原始那個home函式的內地址) #home=wrapper函式的內地址 res = home('egon') # res=wrapper函式的內地址('egon') print(res)
2-2 無參裝飾器(@裝飾器名)
import time def timmer(func): def wrapper(*args, **kwargs): # 讓傳入的引數 原封不動,則可以解決原函式對引數(有參無參)的要求 start_time = time.time() # 開始時間 res = func(*args, **kwargs) # 執行形參函式,並且將func的返回值賦給res stop_time = time.time() # 結束時間 print('run time is %s' % (stop_time - start_time)) print(res) return res # 返回傳入函式的返回值 return wrapper @timmer # 呼叫timmer(foo)並賦值給foo,即foo = timmer(foo) def foo(): time.sleep(3) print('from foo') # 在foo()函式上套上timmer的無參裝飾器 foo() # foo = timmer(foo)
2-2-1 無參裝飾器 - 書寫格式
''' def outter(func): def wrapper(*args,**kwargs) … 邏輯程式碼塊 … res = func(*args,**kwargs) … 邏輯程式碼塊 … return res return wrapper @outter def index(): pass index() '''
2-2-2 無參裝飾器練習 - 認證功能裝飾器
def login_out(func): def login_wrapper(*args, **kwargs): res = func(*args, **kwargs) return res return login_wrapper def users_list(): # 返回值為users_list users_list = [] with open('user_and_pwd', 'rt', encoding='utf-8') as users_pwd_f: users_date = users_pwd_f.read() user_list = users_date.split('/') user_list.remove('') for i in range(len(user_list)): users_name = user_list[i].split('|') users_list.append(users_name) print(users_list) return (users_list) def login_out(func): def login(*args, **kwargs): # 登陸判斷 三次嘗試機會 返回值: '登陸使用者名稱' res = func(*args, **kwargs) count = 0 uname_dic = {} for n in users_list(): uname_dic[n[0]] = n[1] # print(uname_dic) while count < 3: name = input('請輸入使用者名稱:').strip() if name in uname_dic: pwd = input('請輸入密碼:').strip() if pwd == uname_dic[name]: print('登陸成功!\n') uname = name login = True break else: print('使用者名稱不存在!') count += 1 # return (name) print(res) return res # 接受到原函式的返回值 return login @login_out def index(): print('認證功能裝飾器') res = 123 return res index()
2-3 有參裝飾器(@裝飾器名(引數名))
def auth(driver='file'): def auth2(func): def wrapper(*args, **kwargs): name = input("user: ") pwd = input("pwd: ") if driver == 'file': if name == 'egon' and pwd == '123': print('login successful') res = func(*args, **kwargs) return res elif driver == 'ldap': print('ldap') return wrapper return auth2 @auth(driver='file') # 相等於@auth2 但在其內部可以使用dirver的值 def foo(name): print(name) foo('egon')
2-3-1 有參裝飾器書寫格式
# 在無參裝飾器的外層再包一層函式,最外層函式用來進行往內層傳值 ''' 有參裝飾器(書寫格式,做多三層) def out(d='123',a=2,) def outter(func): def wrapper(*args,**kwargs) … 邏輯程式碼塊 … res = func(*args,**kwargs) … 邏輯程式碼塊 … return res return wrapper @out(d='1111') def index(): pass index() '''
2-4 函式上疊加 多裝飾器 的執行順序
''' 解釋呼叫@語法的時候是自下而上執行(即從越靠近函式的執行器先先執行) 執行裝飾器內部函式的wrapper的時候是自上而下 即,func = @outter_最外層(@outter_中間(@outter_內層)) ''' import time def outter1(func1): # func1=wrapper2 print('outter1') def wrapper1(*args, **kwargs): print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 def outter2(func2): # func2=最原始的那個index的記憶體地址 print('outter2') def wrapper2(*args, **kwargs): print('wrapper2') res2 = func2(*args, **kwargs) return res2 return wrapper2 @outter1 # index=outter1(wrapper2) #index=wrapper1 @outter2 # outter2(最原始的那個index的記憶體地址) ===> wrapper2 def index(): print('welcome to index page') time.sleep(3) index() # wrapper1() ''' outter2 outter1 wrapper1 wrapper2 '''
2-5 @wraps 對裝飾器內層函式的使用 -- 讓使用裝飾器的函式 呼叫__name__,__doc__的時候可以指向原有函式屬性
# ------ 不對內層函式使用 @ wraps() - --- import time def outter1(func1): # print('outter1') def wrapper1(*args, **kwargs): # print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 @outter1 def index(): ''' index 功能 ''' print('index page') time.sleep(2) print(index.__name__) # 顯示函式名字 print(index.__doc__) # 顯示函式的註釋內容 # 使用裝飾器之後 會顯示 裝飾器重新複製的函式物件資料 import time from functools import wraps # ------ 對內層函式使用 @ wraps() - --- def outter1(func1): # 讓使用裝飾器的函式 呼叫__name__,__doc__的時候可以指向原有函式屬性 # 若有多層函式巢狀,只需要在最內層的裝飾器新增 @wraps @wraps(func1) def wrapper1(*args, **kwargs): # print('wrapper1') res1 = func1(*args, **kwargs) # res1=wrapper2(*args,**kwargs) return res1 return wrapper1 @outter1 def index(): ''' index 功能 ''' print('index page') time.sleep(2) print(index.__name__) # 顯示函式名字 print(index.__doc__) # 顯示函式的註釋內容
三、 遞迴 - 呼叫函式的過程中直接或者間接呼叫本身
''' 函式的遞迴 : 函式巢狀呼叫的一種特殊形式,在呼叫函式的過程中直接或者間接呼叫本身 遞迴呼叫必須有兩個明確的階段: 1.回溯:一次次遞迴呼叫下去,即一重複的過程 但每次重複,問題的規模減少,直到逼近最終結果 即回溯結果一定有一個明確的結束條件 2.遞推:一層一層的往回傳值 ''' l = [1, [2, [3, [4, [5, [6, ]]]]]] def s(l): for i in l: if type(i) is not list: print(i) else: s(i) s(l)
3-1 二分法
nums = [13, 15, 17, 23, 31, 54, 87, 99, 104, 230] def func(num, search): print(num) if len(num) == 0: print('can not find it') return middle = len(num) // 2 if num[middle] > search: func(num[:middle], search) elif num[middle] < search: func(num[middle:], search) else: print('find it') return func(nums, 3) ''' [13, 15, 17, 23, 31, 54, 87, 99, 104, 230] [13, 15, 17, 23, 31] [13, 15] [13] [] can not find it ''' l = [1, 2, 10, 30, 33, 99, 101, 200, 301, 402] def search(num, l, start=0, stop=len(l) - 1): if start <= stop: mid = start + (stop - start) // 2 print('start:[%s] stop:[%s] mid:[%s] mid_val:[%s]' % (start, stop, mid, l[mid])) if num > l[mid]: start = mid + 1 elif num < l[mid]: stop = mid - 1 else: print('find it', mid) return search(num, l, start, stop) else: # 如果stop > start則意味著列表實際上已經全部切完,即切為空 print('not exists') return search(301, l) ''' start:[0] stop:[9] mid:[4] mid_val:[33] start:[5] stop:[9] mid:[7] mid_val:[200] start:[8] stop:[9] mid:[8] mid_val:[301] find it 8 '''