python函數(shù)、高階函數(shù)、裝飾器、參數(shù)注解、緩存

函數(shù)

1. 函數(shù)的定義

由若干語句組成的語句塊、函數(shù)名稱、參數(shù)列表構成,它是組織代碼的最小單元。

完成一定的功能。

函數(shù)也是對象,python把函數(shù)的默認值放在了屬性中,這個屬性就伴隨著這個函數(shù)對象的整個生命周期。

2. 函數(shù)的作用

  • 結構化編程是對代碼的最基本的<font color = red >封裝</font>,一般按照功能組織一段代碼。
  • 封裝的目的是為了<font color = red >復用</font>,減少冗余代碼。
  • 代碼更加簡潔美觀,可讀易懂。

3. 函數(shù)的分類:

內(nèi)建函數(shù);庫函數(shù);自建函數(shù)

4. 函數(shù)的定義、調用

定義

def語句定義函數(shù)

def 函數(shù)名(參數(shù)列表):

函數(shù)體(代碼塊)

[return 返回值]

定義中的參數(shù)列表成為形式參數(shù),只是一種符號表達,簡稱形參。

定義需要在調用前,否則會拋出NameError異常。


調用

函數(shù)定義,只是聲明了一個函數(shù),它不會被指執(zhí)行,需要調用。

調用的方式,就是函數(shù)名加上小括號,括號內(nèi)寫上參數(shù)。

調用時寫的參數(shù)時實際參數(shù),是實實在在傳入的值,簡稱實參

傳參時位置參數(shù)要放在關鍵字參數(shù)前面。

參數(shù)傳遞:不可變類型,傳遞副本給函數(shù),函數(shù)內(nèi)操作不影響原始值

       可變類型,傳遞的是地址引用,函數(shù)內(nèi)操作可能影響原始值

定義形參和傳遞實參時候的注意事項
  1. 參數(shù)調用時傳入的參數(shù)要和定義的個數(shù)相匹配,可變參數(shù)例外
  2. 定義時,缺省參數(shù)要放在非缺省參數(shù)前。
  3. 定義時加* :可變位置參數(shù):可以收集位置參數(shù)傳入的所有參數(shù),收集多個實參為一個tuple??勺兾恢脜?shù)不能用關鍵字傳參。
  4. 形參加**:可變關鍵字參數(shù),只能用關鍵字傳參??勺冴P鍵字參數(shù),收集的實參名稱和值組成一個字典,所以可修改。
  5. 函數(shù)名也是標識符,返回值也是值,函數(shù)是可調用的對象,callable(函數(shù)名) -> True。
  6. 混合使用參數(shù)的時候,可變參數(shù)要放到參數(shù)列表的最后,普通參數(shù)要放到參數(shù)列表的最前面,可變位置參數(shù)發(fā)要放在可變關鍵字參數(shù)的前面。
  7. keyword-only參數(shù):如果在一個可變位置參數(shù)后面,出現(xiàn)了普通參數(shù),此時這個普通參數(shù)已經(jīng)變成了一個keyword-only參數(shù)
  8. 參數(shù)列表參數(shù)一般順序是,普通參數(shù)、缺省參數(shù)、可變位置參數(shù)、keyword-only參數(shù)(可帶缺省值)、可變關鍵字參數(shù)。
  9. 參數(shù)解構:
    • 給函數(shù)提供實參的時候,可以在集合類型前使用*或者**,把集合類型的結構解開,提取出所有元素作為函數(shù)的實參。
    • 非字典類型使用*解構成位置參數(shù)
    • 字典類型使用**解構成關鍵字參數(shù)
    • 提取出來的元素數(shù)目要和參數(shù)的要求匹配,也要和參數(shù)的類型匹配。

5.函數(shù)的返回值

python函數(shù)使用return語句返回“返回值”。

所有函數(shù)都有返回值。如果沒有return語句,隱式調用return None。

return語句并不一定是函數(shù)的語句塊的最后一條語句

return語句只能執(zhí)行一次,執(zhí)行完,函數(shù)結束,當前return后面的語句就不會再運行了。所以函數(shù)一次只能返回一個值,不能返回多個值,但是可以返回容器,容器里面包含多個值。(return [1,3,5]是指明返回一個列表,是一個列表對象;return 1,3,5看似返回多個值,隱式的被python封裝成一個元組)

作用:結束函數(shù)調用、返回值。

函數(shù)的嵌套

函數(shù)有可見范圍。這就是作用域的概念

外層變量作用域在內(nèi)層作用域可見

內(nèi)部函數(shù)不能在外部直接使用,會拋NameError異常,因為它不可見。

6. 作用域

一個標識符的可見范圍,這就是標識符的作用域。一般常說的是變量的作用域。

全局作用域:在整個函數(shù)運行環(huán)境中都可見。

局部作用域:在函數(shù)、類內(nèi)部可見;局部變量的使用范圍不能超過其所在的局部作用域。

例子:

a = 5
def foo():
    a += 1

foo()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
UnboundLocalError: local variable 'a' referenced before assignment

報錯原因:

a += 1其實就是a = a + 1,a = 5是全局的變量,雖然能在內(nèi)部函數(shù)foo中可見,但是在foo函數(shù)內(nèi)部出現(xiàn)了 a = ,出現(xiàn)等號就是即賦值即重新定義,那么=的右邊作為賦值的內(nèi)容 :a+1,但在函數(shù)中,此時的a已經(jīng)算是重新定義了一個局部變量,而不是用外面的全局變量,但是此時a還沒有完成賦值就被拿來進行加1操作,所以才會報錯。

解決辦法:

在這條語句前增加x=0之類的賦值語句,或者使用global 告訴內(nèi)部作

用域,去全局作用域查找變量定義

默認值的作用域

函數(shù)名.__defaults__屬性:使用元組來保存所有位置參數(shù)默認值,它不會因為在函數(shù)體中使用了它而發(fā)生了變化。

函數(shù)名.__kwdefaults__屬性:使用字典保存所有keyword-only參數(shù)的默認值。

使用可變類型(引用參數(shù))作為默認值,就有可能修改這個默認值。

使用按需修改,例子。

def foo(xyz=[], u='abc', z=123):
    xyz = xyz[:] # 影子拷貝
    xyz.append(1)
    print(xyz)

foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)

# 函數(shù)體內(nèi),不改變默認值
# 使用影子拷貝創(chuàng)建一個新的對象,永遠不能改變傳入的參數(shù)
# xyz都是傳入?yún)?shù)或者默認參數(shù)的副本,如果就想修改原參數(shù),無能為力

def foo(xyz=None, u='abc', z=123):
    if xyz is None:
        xyz = []
    xyz.append(1) 
    print(xyz)

# 使用不可變類型默認值
# 如果使用缺省值None就創(chuàng)建一個列表
# 如果傳入一個列表,就修改這個列表


全局變量global

使用global關鍵字的變量,將函數(shù)內(nèi)的定義的局部變量聲明成全局變量。

如果函數(shù)需要使用外部全局變量,請使用函數(shù)的形參傳參解決。

盡量不使用

nonlocal關鍵字

nonlocal將變量標記為不再本地作用域定義,而在<font color =red>上一級的某一級</font>局部作用域中定義,但不能是全局作用域中定義。

7.<font color = blue>閉包</font>

自由變量:未在本地作用域中定義的變量,例如定義在內(nèi)層函數(shù)外的外層函數(shù)的作用域中的變量

<font color = red >閉包</font>:是一概念,是嵌套函數(shù)中,指的是在內(nèi)層函數(shù)中引用到外層函數(shù)的自由變量,就形成了閉包。

8.變量名解析原則LEGB

  • Local,本地作用域、局部作用域的local命名空間。函數(shù)調用時創(chuàng)建,調用結束消亡。

  • Enclosing,Python2.2時引入了嵌套函數(shù),實現(xiàn)了閉包,這個就是嵌套函數(shù)的外部函數(shù)的命名空間。

  • Global,全局作用域,即一個模塊的命名空間。模塊被import時創(chuàng)建,解釋器退出時消亡。

  • Build-in,內(nèi)置模塊的命名空間,生命周期從python解釋器啟動時創(chuàng)建到解釋器退出時消亡。例如 print(open),print和open都是內(nèi)置的變量。

函數(shù)變量作用域:級別:Built_in(內(nèi)建) > Global(全局) > Enclosing(封裝)> local(本地)

9.函數(shù)的銷毀

全局函數(shù)銷毀
  1. 重新定義同名函數(shù)
  2. del 語句刪除函數(shù)對象名稱,函數(shù)對象的引用計數(shù)減1
  3. 程序結束時
def foo(xyz=[], u='abc', z=123):
    xyz.append(1)
    return xyz
print(foo(), id(foo), foo.__defaults__)
def foo(xyz=[], u='abc', z=123):
    xyz.append(1)
    return xyz
print(foo(), id(foo), foo.__defaults__)
del foo
print(foo(), id(foo), foo.__defaults__)

局部函數(shù)銷毀
  1. 重新在上級作用域定義同名函數(shù)
  2. del 語句刪除函數(shù)名稱,函數(shù)對象的引用計數(shù)減1
  3. 上級作用域銷毀時
def foo(xyz=[], u='abc', z=123):
    xyz.append(1)
    def inner(a=10):
        pass
    print(inner)
    def inner(a=100):
        print(xyz)
    print(inner)
    return inner
bar = foo()
print(id(foo),id(bar), foo.__defaults__, bar.__defaults__)
del bar
print(id(foo),id(bar), foo.__defaults__, bar.__defaults__)

10.遞歸函數(shù)

函數(shù)是需要壓棧的,棧和線程相關。

11.匿名函數(shù)

沒有名字的函數(shù),python借助lamdba表達式構建匿名函數(shù)。

參數(shù)列表不需要小括號。

冒號是用來區(qū)分參數(shù)列表和表達式的。

不需要return,表達式的值,就是匿名函數(shù)返回值。

lambda表達式(匿名函數(shù))只能寫在一行上,被成為單行函數(shù)。

用途:在高階函數(shù)傳參時,使用lambda表達式,往往能簡化代碼

格式:lambda 參數(shù)列表:表達式

   lambda x :  x**2

   (lambda x :  x**2) ()  #調用

12.高階函數(shù)

參時是一個函數(shù),或者輸出一個函數(shù)

13.裝飾器

裝飾器本質上是一個 Python 函數(shù)或類。

它可以讓其他函數(shù)或類在不需要做任何代碼修改的前提下增加額外功能,裝飾器的返回值也是一個函數(shù)/類對象。它經(jīng)常用于有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景,裝飾器是解決這類問題的絕佳設計。有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無關的雷同代碼到裝飾器中并繼續(xù)重用。概括的講,裝飾器的作用就是為已經(jīng)存在的對象添加額外的功能。

多裝飾器的函數(shù)執(zhí)行順序,由底向上

14.參數(shù)注解

文檔注解:函數(shù)內(nèi)的最前面,使用三個雙引號

函數(shù)注解:

  • python3.5引入
  • 對函數(shù)的參數(shù)進行類型注解
  • 對函數(shù)的返回值進行類型注解
  • 只對函數(shù)參數(shù)做一個輔助的說明,并不對函數(shù)參數(shù)進行類型檢查
  • 提供給第三方工具,做代碼分析,發(fā)現(xiàn)隱藏的bug
  • 函數(shù)注解的信息,保存在__annotations__屬性中

變量注解:python3.6引入

函數(shù)參數(shù)類型檢查

思路:

  • 函數(shù)參數(shù)的檢查,一定是在函數(shù)外
  • 函數(shù)應該作為參數(shù),傳入到檢查函數(shù)中
  • 檢查函數(shù)拿到函數(shù)傳入的實際參數(shù),與形參聲明對比
  • __annotations__屬性是一個字典,其中包括返回值類型的聲明,加入要位置參數(shù)的判斷,無法和字典中的聲明對應,使用inspect模塊

inspect模塊:提取獲取對象信息的函數(shù),可以檢查函數(shù)和類、類型檢查

  • inspect.isfunction(add) , 是否是函數(shù)
  • inspect.ismethod(add) , 是否是類的方法
  • inspect.isgenerator(add) , 是否是生成器對象
  • inspect.isgeneratorfunction(add) , 是否是生成器函數(shù)
  • inspect.isclass(add) , 是否是類
  • inspect.ismodule(inspect) , 是否是模塊
  • inspect.isbuiltin(print) , 是否是內(nèi)建對象

signature(callable),獲取簽名(函數(shù)簽名包含了一個函數(shù)的信息,包括函數(shù)名,它的參數(shù)類型,它的所在的類和名稱空間及其他信息)

Parameter對象

保存在元組中

輸入屬性:inspect.signature.parameters.annotation/name/kind/default

返回屬性:inspect.signature.return_annotation

當不知道該方法下面有多少屬性的時候,可以先用type查看該它的類型,然后通過導入模塊,使用參數(shù)注解的方式來查看。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,246評論 0 38
  • 第5章 函數(shù)和函數(shù)式編程 5.1 引言函數(shù)是組織好的,可重復使用的,用來實現(xiàn)單一,或相關聯(lián)功能的代碼段。函數(shù)...
    VIVAFT閱讀 1,083評論 0 5
  • 1. HTML <audio> 標簽 * 注意只有Internet Explorer 9+, Firefox, O...
    卿卿木子七閱讀 1,146評論 2 4
  • 今天是清明假期的第一天。 我隨父母去奠拜爹和婆,爹和婆是我們這里的方言,意思是爺爺和奶奶。 我的爺爺在我很小的時候...
    泥生蓮閱讀 354評論 2 0
  • 這幾天特別累,因為兒子的事,焦頭爛額,為我們之前犯的錯誤進行補救。 現(xiàn)在為了避免讓他產(chǎn)生太多的心...
    汪群_6f58閱讀 104評論 0 0

友情鏈接更多精彩內(nèi)容