看板 Python 關於我們 聯絡資訊
--前言 這文章標題不知道下的好不好 我也沒爬文 如果已有類似觀念 麻煩推文說一下 本來還想把python原文書賣掉 發生這件人為bug後 還是留起來吧XD --內容 這是個關於定義函式時 給定預設值 與 mutable object 的概念 為了簡化實際的案件 下面是一個例子 def show_append(x=[],y=2): x.append(y) print x 寫成這樣... 本來的意思是希望 show_append函式裡的二個變數 x 是個有 append method 的 object y是被 append 的值 如果都沒給值的話 就來個範例 空的list 與 被加入的數2 但很多事情總是不如想像... 下面來三個範例 >>> show_append() [2] >>> show_append() [2, 2] >>> show_append() [2, 2, 2] 這...這飯粒...有毒 跟預想的不一樣 原因是: 在定義函式中 預設的變數(x跟y) 是在定義函式執行時所產生(不是在呼叫的時候) 所以 x (也就是[])是在定義show_append函式時 就一直存在的東西 而因為[] 是mutable obj 在使用 append 這個in-place change的方法時 本身就會改變 所以 要範例的話 可以如下 >>> show_append([]) [2] 給個新的"[]" 就行了 順帶一提 >>> show_append() [2, 2, 2, 2, 2] 原來的預設值還在喲~ 順帶再提 也許你可能會這樣想 如果給x一個新的值[]行不行 像下面 >>> show_append(x=[]) [2] x的預設直就變成空的[]了~?? 當然是不行... 否則假使你用 show_append (y=3) 以後不就都append 3了?(又違反意思了) 在定義函式裡 使用的預設值 比方 def func(name = value_d): ... 呼叫func(name = value) 並不是創建name,並給一個新的值value 所以你不能在呼叫show_append時創建新的變數 像 show_append(another_value = 99) 呼叫時用的name 只是拿來對應func裡的name用而已 (找名字) 預設的value_d值仍然存在 所以 >>> show_append() [2, 2, 2, 2, 2, 2] 一個也沒少喲~ --結論 要注意給函式的預設值是否為mutable,是否符合創造函式的原意 記住python這樣的特性 運用在該用的地方吧!! 記得 力量越大 bug也越大... 共de之 --題外話 總覺得不會碰上的事 總是會碰上...orz 當這樣的問題發生在class裡頭的__init__時候 就更難發現了(我這是這種) 希望不要跟我犯一樣的錯...(還是只有我... 以上 歡迎指正與分享 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.63.202
DigiPrince:http://tinyurl.com/7r3w2y 11/15 17:31
KSJ:看來是只有我了 囧... 11/15 17:45
mathfeel:每次show_append()之后跑help(show_append)。有甚麼發現 11/15 18:10
KSJ:在idle下 show_append( 之後就破梗了 x=會越來越長XD 11/15 18:18
sbrhsieh:Python 中除了 global ...,其實是沒有 definition 的 11/15 20:41
sbrhsieh:這個例子改寫成 C++,不也是一樣的行為嗎? 11/15 23:21
是指說 在runtime才"執行"產生函式嗎@@? 說來殘愧... 我在c與c++寫函式的時候還沒用過default值... 在我初學oop觀念時碰到的是python而非c++ c++的 private public的觀念 或是宣告class的方式 相較於python都較令我難理解 例如c++ . -> 在python下都是 . 理解能力的需求就不一樣 不過當然在c++的程式碼上 可讀性也許不夠 但理解後觀念會更清楚吧 我覺得還是該弄懂c++的類別觀念與寫法 (也許先略懂指標吧... 很多python相關的套件 說明文件還是在c++下較完整 例如PyQt 要了解而使用 以免像我對python def的誤用
mikapauli:不要創造會改變外部物件的函式,就沒這個問題了w 11/16 01:19
mikapauli:一般都會先做適當的copy說 11/16 01:22
其實我還蠻常用把cls instance當做變數在函式(別的class裡的)內傳來傳去 大部份是讀取需要的資料 修改資料的話就直接用method了 (classinstance.method("資料")) 我覺得在函式中修改instance時用instance的method 感覺還ok說 所以我覺得創造修改外部物件的函式應該是需要的 不知道這觀念對不對... 而我是以為class def __init__(self, arg = another_instance_create) 時 會重新創造 default arg裡的東西 所以中招了XD (如1F的文件的 important部份)
sbrhsieh:哎呀,我搞錯了。寫成 C++ 在寫法上沒有等效... 11/16 12:23
kdjf:到python3就很好理解了, 因為function也是class 11/17 09:43
我參考的原文書裡有提了許多 3與2不同的地方 print, string type, type & class, range 等等 不知道function的修改呢~ 有空再來看看:) (這讓我想到 剛學python就抓3下來因為print變成func而無法helloworld的一段往事) 不過我目前使用的相關套件在2下仍有比較好的支援度 所以3還可以再緩一下吧 (我目前是用python2.5較多)
darkgerm:推~寫得好清楚~ 11/19 02:14
※ 編輯: KSJ 來自: 180.176.140.46 (11/19 16:28)
mikapauli:真的需要的話當然OK阿,只是一般函式是沒有side effect的 11/20 21:09