1. 程式人生 > 其它 >裝飾器之多層語法糖

裝飾器之多層語法糖

今日內容概要

  • 多層語法糖內部實質

  • 有參裝飾器

  • 遞迴函式

  • 演算法之二分法

 

今日內容詳細

一、多層語法糖內部實質

"""語法糖會將緊挨著的被裝飾物件的名字當做引數自動傳入裝飾器函式中""" 比如舉個例子來解釋一下這段化的意思:
   判斷下面七句print執行的順序即可搞懂多層語法糖的用法
   多層語法糖如何解讀:裝飾順序由下往上 遇到最後一個才會是使用與函式名相同的變數名傳給裝飾器函式呼叫
def outter1(func1):
   print('載入了outter1')
   def wrapper1(*args, **kwargs):
       print('執行了wrapper1')
       res1 = func1(*args, **kwargs)
       return res1
   return wrapper1

def outter2(func2):
   print('載入了outter2')
   def wrapper2(*args, **kwargs):
       print('執行了wrapper2')
       res2 = func2(*args, **kwargs)
       return res2
   return wrapper2

def outter3(func3):
   print('載入了outter3')
   def wrapper3(*args, **kwargs):
       print('執行了wrapper3')
       res3 = func3(*args, **kwargs)
       return res3
   return wrapper3


@outter1
@outter2
@outter3
def index():
   print('from index')
   
   

 

 

二、有參裝飾器

def outer(source_data):
   def login_auth(func_name):
       def inner(*args,**kwargs):
           username =input('username>>>>:').strip()
           password =input('password>>>>>:').strip()
# 思路:1.判斷使用者資料 資料的來源可以有很多 比如全域性字典 全域性列表 文字檔案 資料庫等
2.因為資料的來源不同 那麼問題來了相應的處理方式也是不同的 那麼對應的程式碼編寫也是不一樣的
3.根據不同的引數提示 需要匹配不同的流程 這裡我可以聯想到用分支結構處理
            if source_data =='1':
      print('使用字典的方式處理資料')
            elif source_data =='2':
                   print('使用列表的方式處理資料')
            elif source_data == '3':
               print('使用檔案操作處理資料')
             else
                print('其他操作情況')
               res = func_name(*args,**kwargs)
              return res
       return inner
   return login_auth
'''''''
函式名加括號 執行優先順序最高

@outer('3')
  左側是@outer是語法糖結構 右側outer('3')是函式名加括號結構
   根據函式名加括號執行優先順序最高,所以先執行函式呼叫 outer('3') 返回值是login_auth
   在執行語法糖結構 @login_auth
   由此發現還是一個普通的裝飾器
有參裝飾器目的僅僅是給裝飾器傳遞額外的引數 原因是在其他函式形參中都不能動了 都不能添加了,所以只能新定義一個函式 專門新增額外的引數

三、遞迴函式

# 本質:遞迴函式也稱為函式的遞迴
 函式在執行過程中直接或間接的呼叫了自身
 大白話:自己呼叫了自己
def index():
   print('from index')
   index()
index()   # 會報錯 maximum recursion depth exceeded while calling a Python object
""""""  表示:最大遞迴深度超出限制了 python直譯器自帶的應急機制
在有些程式語言中 甚至沒有遇警機制 程式碼執行真的會一直到計算機崩潰為止


間接呼叫自己
def index():
   print('from index')
   func()
def func():
   print('from func')
   index()

func()
""'""'
maximum recursion depth exceeded while calling a Python object
   這對python最大遞迴深度 回答 997 998 1000都可以
   官方給出的是1000
   
   import sys
   print(sys.getrecursionlimit())  # 獲取預設的最大遞迴深度 # 1000
   sys.setrecursionlimit(2000)  # 還可以改最大遞迴深度
   比如:
   count = 1
   def index():
       print('from index')
       global count
       print(count)
       count += 1
       index()
    index()
   
   """"""
   函式的遞迴不應該是無限迴圈的過程 真正的遞迴函式應該滿足兩個要求:
   1.每次遞迴 複雜度必須降低(下一次遞迴要比上一次遞迴簡單)
   大白話就是說越往下遞迴應該離解決問題的答案越近
   2.必須是有要有明確的結束條件
   
   

   
   


 

 

四、 遞迴函式的應用

需求:我們想知道坐在第一排的某個學生年齡過程:
我們問他多大了 他沒有直接告知我們 說比後面那個同學大兩歲
  後面的同學說比他後面的大兩歲
   問到最後一排時 終於開口說是18歲
   知道租後一排的年齡回推即可知道第一排那個同學的年齡
   遞推:是指一層層往下推
   回溯:根據最後的結果去推結論
   
# 如何編寫程式碼完成
# 目標人物的年齡 = 後一排的年齡 +2
# 後一排的年齡 = 後後一排年齡 +2
# 後後一排的年齡 = 後後後一排的年齡 + 2
# 最後一排的年齡 = 18
 # 將年齡問題程式設計程式碼函式
# age(5) = age(4) +2
# age(4) = age(3) +2
# age(3) = age(2) +2
# age(2) = age(1) +2
# age(1) = 18

將以上程式碼封裝成函式
def get_age(n):
   if n == 1:
       return 18  # 有明確的結束條件
    return get_age(n-1) +2
print(get_age(4))
""注意:對於遞迴函數千萬不要想的太複雜 只要記住巢狀圖即可 有結果就是由內而外一層層的剝開
   

五、遞迴函式練習

l1 = [1,[2,[3,[4,[5,[6,[7,[8,[9]]]]]]]]]
""需求:迴圈打印出列表中每一個數字
# 寫程式碼之前一定要先理清思路

完整思路:
1.for迴圈大列表
2.判斷元素是否是數字 如果是則列印
3.如果不是則for迴圈
4.判斷元素是否是數字 如果是則列印
5.如果不是則for迴圈
6.判斷元素是否是數字 如果是則列印
7.如果不是則for迴圈
注意:重複做一些事情 但是每次都比上一次簡單一些 >>>>>聯想到遞迴函式
def get_num(l):
   for i in l:  # 因為for自帶結束條件 並且每次傳入的資料都比上一次簡單
       if isinstance(i,int):  # 判斷某個資料是否屬於某個型別
           print(i)
        else:
           get_num(i)
get_num(l1)


六、演算法之二分法

# 什麼是演算法?
演算法其實就是解決問題的有效方法
eg:比如開啟易拉罐的問題
    方法1:使用金屬撬棍
       方法2:直接手扣
       方法3:一陽指戳
      ...
 """
演算法比較偏向於學術研究 很枯燥 並且產出很少
甚至只有非常大的網際網路公司才會有演算法部分
演算法工程師薪資待遇很高 但是產出很少
有時候甚至幾年都沒有任何的成果 有點類似於研究所!!!
"""

# 演算法之二分法
二分法是演算法裡面最入門的一個 主要是感受演算法的魅力所在
 # Author:Jason
"""二分法使用有前提: 資料集必須有先後順序(升序 降序)"""
l1 = [13,21,35,46,52,67,76,87,99,123,213,321,432,564,612]
# 查詢一個數 123
"""
二分法原理
  獲取資料集中間的元素 比對大小
      如果中間的元素大於目標資料 那麼保留資料集的左邊一半
      如果中間的元素小於目標資料 那麼保留資料集的右邊一半
  然後針對剩下的資料集再二分
      如果中間的元素大於目標資料 那麼保留資料集的左邊一半
      如果中間的元素小於目標資料 那麼保留資料集的右邊一半
  ...
"""
def get_target(l1,target_num):
   # 最後需要考慮找不到的情況 l1不可能無限制二分
   if len(l1) == 0:
       print('不好意思 真的沒有 找不到')
       return
   # 1.獲取中間元素的索引值(只能是整數)
   middle_index = len(l1) // 2
   # 2.判斷中間索引對應的資料與目標資料的大小
   if target_num > l1[middle_index]:
       # 3.保留資料集右側
       l1_left = l1[middle_index+1:]
       # 3.1.對右側繼續二分 重複執行相同程式碼 並且複雜度降低
       print(l1_left)
       get_target(l1_left,target_num)
   elif target_num < l1[middle_index]:
       # 4.保留資料集左側
       l1_right = l1[:middle_index]
       print(l1_right)
       # 4.1.對右側繼續二分 重複執行相同程式碼 並且複雜度降低
       get_target(l1_right,target_num)
   else:
       print('找到了',target_num)
# get_target(l1,432)
# get_target(l1,22)
get_target(l1,13)
"""
二分法的缺陷
  1.如果要找的元素就在資料集的開頭 二分更加複雜
  2.資料集必須有順序
目前沒有最完美的演算法 都有相應的限制條件
"""
"""
以後面試的時候 可能會讓你手用python寫一些演算法
  二分法 快排 插入 冒泡 堆排序
      上述知識面試之前臨時抱佛腳即可 平時無需過多研究
"""