2 回答

TA貢獻165條經驗 獲得超90個贊
>>>?len(range(2)) 2 >>>?a=len >>>?a(range(2)) 2
每次看到別人問關于 函數傳遞,調用,返回等問題時,我就很開心,哈哈。 因為我也是從這里開始出發,迷上函數式編程的。感覺它比 OOP 更加抽象,也更加有意思。
好了不廢話了, 開始解題。 首先說上面的代碼: 第三行, 我們把len函數 賦值給了一個變量a, 接著在第四行,就可以調用函數a 了。 這說明函數本身和 Python 的其他對象一樣(如,數字 1,2;字符串:'aa' 等)是可以作為值去賦值給其他變量名的, 同時 函數也就可以作為 另一個函數的返回值。
def?make_adder(addend): ????def?adder(augend): ????????return?augend?+?addend ????return?adder ???? p?=?make_adder(23) print?(make_adder(23))????????????????????#(1) print?(p)?????????????????????????????????#(2) print?(p(100))????????????????????????????#(3) print?(p.__closure__)?????????????????????#(4) print?(p.__closure__[0].cell_contents)????#(5) ##下面是這5次?輸出,?請自動修正我用的?Python3.x?print()?語法 #(1)?<function?make_adder.<locals>.adder?at?0x0000019AE2F2E950> #(2)?<function?make_adder.<locals>.adder?at?0x0000019AE2F2E8C8> #(3)?123 #(4)?(<cell?at?0x0000019AE2BA8C18:?int?object?at?0x000000006767F1F0>,) #(5)?23
好, 我們依次解釋這五次輸出,順便講下 幾乎所有支持函數式編程的語言 都會有的概念: 閉包。這大概也是面試官最愿意問的問題吧:順嘴一提,面試者隨便一說,面試官點點頭,能最大程度緩解氣氛 是不? 哈哈, 反正我做面試的時候會問。
因為, make_adder 這個函數的返回值 是一個函數, 并且調用這個函數時,必須傳遞一個參數(后面這個參數的值,就成為了p 的閉包中很重要的組成部分)。所以第一個print 很好理解,就是一個函數體(把在函數中定義的函數 adder作為值 返回了)。
這第二個print 就和開篇的例子一個道理,講一個函數作為值賦值給一個變量名了, 那么變量p 也是一個函數體。
至于第三個,就是對于函數p 的調用, 自然 打印的就是這個函數執行的結果,或者說是返回的結果。
好,從這里開始就是閉包了,閉包 和 詞法作用域(lexical scoping)的概念,復制過來也就是幾句簡單但是不怎么容易理解的話, 這里就不贅述了,想了解請用搜索引擎。簡單來說就是,當一個函數被定義時,它擁有的不僅僅是定義時的那些邏輯代碼 還 擁有當時(定義時)的變量環境。 Python作為一個以優美著稱的語言,必須 可以拿出解釋這個概念的直觀方法, 那就是每個函數 都有一個 閉包屬性, 這第四次print 就是打印出了 函數的這個屬性, 它是一個元組,其元素是 cell 類型。
第五次 print ,就很簡單了, 就是打印出了, 函數的 閉包(tuple) 的第一個元素(cell)的內容(cell_contents)。
說了這么多, 也不知道題主理解了沒有??偟膩碚f, 正是由于 函數擁有閉包的這屬性, 才能在 其定義的外部調用時, 不會忘記 或者找不到 其應該 附帶的一些變量(此例中的 23)。
如果將函數改成:
def?make_adder(addend): ????a?=?100 ????def?adder(augend): ????????return?augend?+?addend?+?a ????return?adder
五次輸出有什么不同呢? 請自行實驗,對比,領悟 Python 中 函數的閉包屬性。

TA貢獻12條經驗 獲得超18個贊
1、return adder ,輸出adder的內存地址
2、p = return adder , 同樣是輸出adder的內存地址
3、print make_adder(23)(100),調用adder函數返回23+100的值,這里23取的是make_adder函數的參數值,100是adder函數的參數值
添加回答
舉報