Python中看似沒用的寫法,卻是老手都不一定會的
一次無意間看到如下的代碼:
心想:咦?這不是脫褲子放屁嗎?函數(shù)里面直接使用變量就好了,非要定義成函數(shù)參數(shù)。
結(jié)果沒想到這是解決問題的關(guān)鍵。今天我們研究一下這玩意到底解決什么問題以及它的原理。
現(xiàn)在我們從最簡單的函數(shù)使用外部變量的情況開始:
為了在函數(shù)中使用外部的變量,這是最直觀的做法。這種在函數(shù)中直接使用外部定義的變量,還有一種叫法:'閉包'。
我相信就算不了解 python 查找變量規(guī)則的初學(xué)者,也能一下子理解函數(shù)執(zhí)行后會輸出什么。因為 python 就是為了讓其符合直覺才把規(guī)則設(shè)計成這樣。
現(xiàn)在稍微修改一下代碼:
在函數(shù)執(zhí)行之前,修改了外部的變量,大家認為函數(shù)執(zhí)行后打印了什么?
看看結(jié)果:
不知道你猜對了沒有,不過我是覺得這個結(jié)果同樣符合直覺。
你也覺得結(jié)果符合直覺嗎?
這是因為函數(shù)里面使用外部變量,就是要表達:“執(zhí)行 print 時,獲取變量此時此刻的值。
那么,現(xiàn)實中會不會出現(xiàn)一些場景,我們就是希望函數(shù)執(zhí)行時,得到的是 創(chuàng)建函數(shù)的時候,外部變量的值,而非執(zhí)行時刻的值 ?
沒錯,就是文章開篇的寫法:
真的存在這樣子的場景嗎?而且,這是什么原理?
我們可以歸納以上代碼的特點:
- 定義了函數(shù)
- 函數(shù)內(nèi)部,希望使用外部定義的變量
- 定義函數(shù)后,并沒有立刻執(zhí)行,并且當(dāng)函數(shù)執(zhí)行的時候,使用的外部變量很可能已經(jīng)被修改了
由于 python 寫交互的程序不多,一個函數(shù)的執(zhí)行時機基本上都是我們使用代碼明確編寫。但是大概有2種例外情況:
- 把函數(shù)交給別的調(diào)度器,在合適時機執(zhí)行。比如多線程多進程
- 在界面編程中,綁定各種事件。事件函數(shù)只會在用戶與界面交互時才被觸發(fā)執(zhí)行
在這些場景中,最容易出現(xiàn)的情況是,在一個循環(huán)遍歷中,定義函數(shù),綁定函數(shù)。下面是一個循環(huán)創(chuàng)建10個按鈕,點擊時界面出現(xiàn)提示信息:
上面的代碼創(chuàng)建了10個不同的函數(shù)對象,可惜的是,行7的變量 idx 是外部的變量 idx(行4),并且在循環(huán)執(zhí)行過程中,idx 的值不斷增加,最終的值停留在 9。
因此,界面上不管點擊哪個按鈕,顯示信息都是 9
現(xiàn)在,我們使用之前學(xué)會的套路,定義函數(shù)參數(shù)默認值解決:
我特意讓參數(shù)名與外部變量不一致,這更容易理解原理。
到底為什么這樣子寫可以解決問題,我們不妨把循環(huán)給展開(只展開2次):
注意行15 與 行23 ,定義函數(shù)的時候,我們把此刻的 idx 值,付給了參數(shù) num 作為默認值。相當(dāng)于如下代碼:
此時,這個默認值不再隨 idx 修改而改變。所以每個按鈕綁定的函數(shù),看似代碼邏輯是一模一樣,但是每個函數(shù)的參數(shù) num 都是不一樣的值。
你學(xué)會了嗎?