Python第四周 學習筆記(1)

來源:互聯網
上載者:User

標籤:學習筆記

函數
  • Python的函數沒有return語句,隱式會返回一個None值
  • 函數是可調用的對象,callable()
函數參數
  • 參數調用時傳入的參數要和定義的個數相匹配(可變參數例外)
  • 位置參數
    • def f(x, y, z) 調用使用 f(1, 3, 5)
    • 按照參數定義順序傳入實參
  • 關鍵字參數
    • def f(x, y, z) 調用使用 f(x=1, y=3, z=5)
    • 使用形參的名字來出入實參的方式,如果使用了形參名字,那麼傳參順序就可和定義順序不同
  • 傳參
    • 要求位置參數必須在關鍵字參數之前傳入,位置參數是按位置對應的
函數參數預設值
  • 定義時,在形參後跟上一個值
  • 作用
    • 參數的預設值可以在未傳入足夠的實參的時候,對沒有給定的參數賦值為預設值
    • 參數非常多的時候,並不需要使用者每次都輸入所有的參數,簡化函數調用
可變參數
  • 一個形參可以匹配任意個參數

  • 在形參前使用*表示該形參是可變參數,可以接收多個實參
  • 收集多個實參為一個tuple

  • 關鍵字參數的可變參數

    • 形參前使用**符號,表示可以接收多個關鍵字參數
    • 收集的實參名稱和值組成一個字典
  • 有位置可變參數和關鍵字可變參數
  • 位置可變參數在形參前使用一個星號*
  • 關鍵字可變參數在形參前使用兩個星號**
  • 位置可變參數和關鍵字可變參數都可以收集若干個實參,位置可變參數收集形成一個tuple,關鍵字可變參數收集形成一個dict
  • 混合使用參數的時候,可變參數要放到參數列表的最後,普通參數需要放到參數列表前面,位置可變參數需要在關鍵字可變參數之前
keyword-only參數
  • 如果在一個星號參數後,或者一個位置可變參數後,出現的普通參數,實際上已經不是普通的參數了,而是keyword-only參數
def fn(*args, x):print(x)print(args)
def(**kwargs, x):print(x)print(kwargs)

直接報語法錯誤
可以理解為kwargs會截獲所有的關鍵字參數,就算你寫了x=5,x也永遠得不到這個值,所以語法錯誤

  • keyword-only 參數另一種形式
    def fn(*, x,y):print(x,y)fn(x=5,y=6)

    *號之後,普通形參都變成了必須給出的keyword-only 參數

可變參數和參數預設值
def fn(*args, x=5):print(x)print(args)
參數規則
  • 參數列表參數一般順序是,普通參數、預設參數、可變位置參數、keyword-only參數(可帶預設值)、可變關鍵字參數
參數解構
  • 給函數提供實參的時候,可以在集合類型前使用*或者**,把集合類型的結構解開,提取出所有元素作為函數的實參
  • 非字典類型使用*解構成位置參數
  • 字典類型使用**解構成關鍵字參數
  • 提取出來的元素數目要和參數的要求匹配,也要和參數的類型匹配

  • 參數解構和可變參數
    • 給函數提供實參的時候,可以在集合類型前使用*或者**,把集合類型的結構解開,提取出所有元素作為函數的實參
函數傳回值與範圍函數的傳回值
  • Python函數使用return語句返回“傳回值”
  • 所有函數都有傳回值,如果沒有return語句,隱式調用return None
  • return 語句並不一定是函數的語句塊的最後一條語句
  • 一個函數可以存在多個return語句,但是只有一條可以被執行。如果沒有一條return語句被執行到,隱式調用return None
  • 如果有必要,可以顯示調用return None,可以簡寫為return
  • 如果函數執行了return語句,函數就會返回,當前被執行的return語句之後的其它語句就不會被執行了
  • 作用:結束函數調用、傳回值

  • 返回多個值
    • 函數不能同時返回多個值
    • return [1, 3, 5] 是指明返回一個列表,是一個列表對象
    • return 1, 3, 5 看似返回多個值,隱式的被python封裝成了一個元組
      def showlist():return 1, 3, 5x, y, z = showlist() # 使用解構提取更為方便
函數嵌套
  • 在一個函數中定義了另外一個函數
    • 函數有可見範圍,這就是範圍的概念
    • 內建函式不能在外部直接使用,會拋NameError異常,因為它不可見
範圍
  • 全域範圍

    • 在整個程式運行環境中都可見
  • 局部範圍

    • 在函數、類等內部可見
    • 局部變數使用範圍不能超過其所在的局部範圍
  • 外層變數範圍在內層範圍可見
  • 內層範圍inner中,如果定義了o=97,相當於當前範圍中重新定義了一個新的變數o,但是這個o並沒有覆蓋外層範圍outer中的o

  • 全域變數global
    #x = 5def foo():global xx = 10x += 1 # 報錯嗎?print(x) # 列印什嗎?print(x) #列印什嗎?
  • 使用global關鍵字的變數,將foo內的x聲明為使用外部的全域範圍中定義的x
  • 但是,x = 10 賦值即定義,在內部範圍為一個外部範圍的變數x賦值,不是在內部範圍定義一個新變數,所以x+=1不會報錯。注意,這裡x的範圍還是全域的

  • x+=1這種是特殊形式產生的錯誤的原因?先引用後賦值,而python動態語言是賦值才算定義,才能被引用。解決辦法,在這條語句前增加x=0之類的指派陳述式,或者使用global 告訴內部範圍,去全域範圍尋找變數定義
  • 內部範圍使用x = 5之類的指派陳述式會重新定義局部範圍使用的變數x,但是,一旦這個範圍中使用global聲明x為全域的,那麼x=5相當於在為全域範圍的變數x賦值

  • global使用原則
    • 外部範圍變數會內部範圍可見,但也不要在這個內部的局部範圍中直接使用,因為函數的目的就是為了封裝,盡量與外界隔離
    • 如果函數需要使用外部全域變數,請使用函數的形參傳參解決
    • 一句話:不用global。學習它就是為了深入理解變數範圍
閉包(重要概念)
  • 自由變數:未在本地範圍中定義的變數。例如定義在記憶體函數外的外層函數的範圍中的變數
  • 閉包:就是一個概念,出現在嵌套函數中,指的是內層函數引用到了外層函數的自由變數,就形成了閉包。很多語言都有這個概念,最熟悉就是JavaScript
  • 使用global可以解決,但是這使用的是全域變數,而不是閉包
  • 如果要對普通變數的閉包,Python3中可以使用nonlocal
nonlocal關鍵字
  • 使用了nonlocal關鍵字,將變數標記為不在本地範圍定義,而在上級的某一級局部範圍中定義,但不能是全域範圍中定義
預設值的範圍
  • 函數也是對象,python把函數的預設值放在了屬性中,這個屬性就伴隨著這個函數對象的整個生命週期

  • 屬性defaults中使用元組儲存所有位置參數預設值,它不會因為在函數體內使用了它而發生改變
  • 屬性kwdefaults中使用字典儲存所有keyword-only參數的預設值

  • 使用可變類型作為預設值,就可能修改這個預設值
  • 有時候這個特性是好的,有的時候這種特性是不好的,有副作用

  • 第一種方法
    • 使用影子拷貝建立一個新的對象,永遠不能改變傳入的參數
      def foo(xyz=[], u=‘abc‘, z=123):xyz = xyz[:] # 影子拷貝xyz.append(1)print(xyz)foo()print(foo.__defaults__)foo()print(foo.__defaults__)foo([10])print(foo.__defaults__)foo([10,5])print(foo.__defaults__)

函數體內,不改變預設值
xyz都是傳入參數或者預設參數的副本,如果就想修改原參數,無能為力

  • 第二種方法
    • 通過值的判斷就可以靈活的選擇建立或者修改傳入對象
    • 這種方式靈活,應用廣泛
    • 很多函數的定義,都可以看到使用None這個不可變的值作為預設參數,可以說這是一種慣用法
def foo(xyz=None, u=‘abc‘, z=123):    if xyz is None:        xyz = []    xyz.append(1)    print(xyz)foo()print(foo.__defaults__)foo()print(foo.__defaults__)foo([10])print(foo.__defaults__)foo([10,5])print(foo.__defaults__)

使用不可變類型預設值
如果使用預設值None就建立一個列表
如果傳入一個列表,就修改這個列表

變數名解析原則LEGB
  • Local,本地範圍、局部範圍的local命名空間。函數調用時建立,調用結束消亡
  • Enclosing,Python2.2時引入了嵌套函數,實現了閉包,這個就是嵌套函數的外部函數的命名空間
  • Global,全域範圍,即一個模組的命名空間。模組被import時建立,解譯器退出時消亡
  • Build-in,內建模組的命名空間,生命週期從python解譯器啟動時建立到解譯器退出時消亡。例如 print(open),print和open都是內建的變數
  • 所以一個名詞的尋找順序就是LEGB

函數的銷毀
  • 全域函數銷毀

    • 重新定義同名函數
    • del 語句刪除函數對象
    • 程式結束時
  • 局部函數銷毀
    • 重新在上級範圍定義同名函數
    • del 語句刪除函數名稱,函數對象的引用計數減1
    • 上級範圍銷毀時
遞迴
  • 函數直接或者間接調用自身就是遞迴
  • 遞迴需要有邊界條件、遞迴前進段、遞迴返回段
  • 遞迴一定要有邊界條件
  • 當邊界條件不滿足的時候,遞迴前進
  • 當邊界條件滿足的時候,遞迴返回

  • 遞迴要求

    • 遞迴一定要有推出條件,遞迴調用一定要執行這個允出準則。沒有允出準則的遞迴調用,就是無限調用
    • 遞迴調用的深度不宜過深
    • Python對遞迴調用的深度做了限制以保護解譯器
    • 超過遞迴深度限制,拋出RecursionError maxinum recursion depth exceeded 超出最大深度sys.getrecursionlimit()
  • 遞迴的效能

    • 迴圈稍微複雜一些,但是只要不是死迴圈,可以多次迭代直至算出結果
    • 遞迴有深度限制,如果遞迴複雜,函數反覆壓棧,占記憶體很快就溢出了
  • 間接遞迴

    • 通過別的函數調用了函數自身
    • 但是,如果構成了迴圈遞迴調用是非常危險的,但是往往在代碼複雜的情況下,還是可能發生這種調用。要用代碼的規範來避免這種遞迴調用的發生
  • 總結
    • 遞迴是一種很自然地表達,符合邏輯思維
    • 遞迴相對運行效率低,每一次調用函數都要開闢棧幀
    • 遞迴有深度限制,如果遞迴層次太深,函數反覆壓棧,棧記憶體很快就溢出了
    • 如果是有限次數的遞迴,可以使用遞迴,或者使用迴圈代替,迴圈代碼稍微複雜一些,但是只要不是死迴圈,可以多次迭代直至算出結果
    • 絕大多數遞迴,都可以使用迴圈實現
    • 即使遞迴代碼很簡潔,但是能不用則不用遞迴
匿名函數
  • 使用Lambda運算式構建匿名函數
  • 格式

    • lambda 參數列表:運算式
  • 使用lambda關鍵字來定義匿名函數
  • 參數列表不需要小括弧
  • 冒號是用來分割參數列表和運算式的
  • 不需要使用return,運算式的值,就是匿名函數傳回值
  • lambda運算式(匿名函數)只能寫在一行上,被稱為單行函數

  • 用途
    • 在高階函數傳參時,使用lambda運算式,往往能簡化代碼
[x for x in (lambda *args: map(lambda x: x+1, args))(*range(5))] [x for x in (lambda *args: map(lambda x: (x+1,args), args))(*range(5))]

Python第四周 學習筆記(1)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.