前言
那 JavaScript 中有一個東西叫做閉包 (Closure),Python 也會有嗎?所以這章節就輕鬆的聊一下如果你想要在 Python 中寫閉包的話該如何撰寫。
變數作用域
首先先簡單回顧聊一下前面的「從 JavaScript 角度學 Python(6) - 變數作用域」章節,我們都知道每一個變數都有屬於它自己的作用域,例如:全域變數、區域變數等等。
那麼如果拿全域變數來講的話,你只要放在程式碼的最外層,就一定可以透過類似 JavaScript 的範圍鍊概念向上尋找到變數:
1 2 3 4 5 6
| money = 1
def fn(): print(money)
fn()
|
至於區域變數呢?前面都有說過 Python 的變數類似 JavaScript 的 var 變數,因此會是以 function 為作用域,因此宣告在函式內的變數我們就無法在這個函式之外取得:
1 2 3 4 5 6
| def fn(): money = 1 print(money)
fn() print(money)
|
好的,我們複習差不多了,今天大概就到這邊…
再騙我啊!
別急,我們只是快速複習一下變數作用域的部分,接下來就讓我們準備了解 Python 的閉包該怎麼寫吧。
閉包(Closure)
那麼在了解並開始撰寫 Python 閉包之前,讓我先擷取我先前寫的 JavaScript 閉包範例程式碼:
1 2 3 4 5 6 7 8 9 10
| function count() { let icash = 1000; return function (price){ icash = icash - price; return icash; } } let ray = count(); ray(100); ray(200);
|
上面是一個非常簡單的 JavaScript 閉包概念,在正常情況下 count 函式中的 icash 變數會因為被內部匿名函式所引用的關係,而不會被釋放掉記憶體,藉此可以讓我們重複使用這個變數記憶體,如果對於 JavaScript 的閉包觀念不熟悉的話,建議可以先閱讀我這一篇「JavaScript 核心觀念(38)-函式以及 This 的運作-閉包 Closure」文章。
那這邊讓我們拉回到 Python 中,如果想要寫出跟上面 JavaScript 的閉包我們又給如何撰寫呢?讓我們看一下將上面程式碼改成 Python 的話會是長什麼樣子呢?:
1 2 3 4 5 6 7 8 9 10
| def count(): icash = 1000 def reduce(price): nonlocal icash icash = icash - price return reduce
ray = count() ray(100) ray(200)
|
oh!這邊你可千萬不要這樣子寫:
1 2 3 4 5 6 7 8 9
| def count(): icash = 1000 return def (price): nonlocal icash icash = icash - price
ray = count() ray(100) ray(200)
|
如果你嘗試改得像 JavaScript 一樣的話只會得到一個 SyntaxError: invalid syntax 的錯誤唷。
那麼透過上面的程式碼示範之後也了解到閉包的概念不外乎也與巢狀函式脫離不了關係。
工廠模式
那麼我們都知道 JavaScript 的閉包進階用法還有一個工廠模式,透過工廠模式我們可以有一些操作方法,例如顯示目前的金額,所以這邊也擷取我先前寫的 JavaScript 閉包工廠模式的範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| function myMoney(storage) { var money = storage; return function(price) { return { nowMoney: function () { return console.log(money); }, count: function (price) { if(money < price) return console.log('餘額不足,目前餘額: ' + money + ' $'); if (!money <= 0) { return money = money - price; } return console.log('餘額扣除失敗,目前餘額: ' + money + ' $'); } } } }
var ming = myMoney(500);
var mei = myMoney(5000);
var wang = myMoney(30000);
ming().count(100); ming().count(100); ming().count(300);
ming().nowMoney();
mei().count(1600); mei().count(100); mei().count(600);
mei().nowMoney();
wang().count(300);
wang().nowMoney();
|
那麼問題來了 Python 可以寫出一樣的功能嗎?答案是可以的,只是在寫法上必須做一下調整:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| def myMoney(storage): icash = storage
def nowMoney(): return print('目前餘額:', icash)
def count(price): nonlocal icash icash = icash - price return print('金額扣除後剩餘:', icash)
return { 'nowMoney': nowMoney, 'count': count, }
ming = myMoney(500)
mei = myMoney(5000)
wang = myMoney(30000)
ming['count'](100) ming['count'](100) ming['count'](300)
ming['nowMoney']()
mei['count'](1600) mei['count'](100) mei['count'](600)
mei['nowMoney']()
wang['count'](300)
wang['nowMoney']()
|
雖然與 JavaScript 範例稍微有點不同,我是直接回傳一個字典,但是依然可以達到閉包的效果,因此我們也可以再次驗證只要變數有被持續引用的狀況下,那麼變數記憶體就不會被回收掉,而持續可以被使用唷。
那麼學到現在有沒有再一次體會到想要無痛轉 Python 是有可能的呢?而且 Python 學起來真的很簡單呢~
快逃
參考文獻
作者的話
前幾天朋友問我說你最近在忙什麼,我跟他說我忙著準備鐵人賽文章,結果他跟我說現在跑三鐵還要寫文章?
我:????
關於兔兔們
兔法無邊