發表文章

目前顯示的是 4月, 2018的文章

[Python] *args 和 **kwargs 是什麼?一次搞懂它們!

在翻閱 Python 的函式庫時常常會看到定義參數的地方放了 *args 和 **kwargs 這樣的東西,這究竟是什麼呢?讓我們先談談函式參數的定義。 預設參數   一般的定義方法就不多說了,直接來看有預設值的參數: def plus(a, b, c=None): res = a + b + (c if c else 0) return res   預設參數的用處通常是實作函式重載用的,可以使一個函式在接受引數時更有彈性,而要注意的語法問題是:預設參數在函式定義時一定要放在非預設參數的後面。   但如果我們想實作無限版的 plus() 函式呢?總不可能一直增加預設參數吧!   這時候我們可以用「*」來將引數收集到一個 tuple 中。 * -收集至 Tuple   先來看看範例: def plus(*nums): res = 0 for i in nums: res += i return res   透過 * 收集的引數會被放到一個 tuple 中,所以我們可以使用 for 來對它進行迭代。   這樣就可以理解為什麼要使用 *args 這個參數了,但是 **kwargs 又是什麼呢?我們要先從關鍵字引數來說起: 關鍵字引數 Keyword Argument   在呼叫 print() 時,我們有時會指定 sep 參數做為分隔輸出的字元,或是使用 end 參數來更改最後的換行字元。像這樣不用理會參數的真正順序,而只要給定名字然後指定值的情況,就是在使用關鍵字引數。   如果我們要指定的參數太多而造成版面不簡潔的話,可以考慮使用「**」來拆解一個裝有參數名與值的 dict。 ** 第一招-拆解 Dict   原諒我使用這麼中二的小標題XDD   直接看實例應該就能懂了: dt = {'sep': ' # ', 'end': '\n\n'} print('hello', 'world', **dt) # 等同於 print('hello', 'world', sep=' # ', end=&

[Python] 關於變數與參考的二三事

圖片
寫過 Python 的人大概都知道,在複製 list 的時候最好不要直接指定,而要使用 copy 函式,但可能有些時候,我們還是會不小心觸發這個黑魔法,所以今天我們要來破解這個魔咒,看看到底背後藏了什麼祕密! 變數   首先我們要來看一下 Python 的變數到底是如何運作的,假設我們輸入了: x = 1   就代表我們把 x 的值指定為 1 了, 是嗎? 事實上並不是這樣的,而是讓 x 這個變數參考到了 1 這個物件,我們可以用 id() 函式來看看這個物件在記憶體中的位址究竟在哪: print(id(x)) # 2011157552   我們可以看到,有一個奇怪的數字被印出來了。而如果我們將 x 加上 1,也就是 x += 1 之後,再對 x 使用 id() 的話,我們會發現,輸出結果,也就是物件的位址變得不同了,這似乎和 C++ 這類的靜態語言很不一樣。   這個差別很重要嗎?可能在處理數值型態如整數之類的型態不太需要去注意,但在處理 list -也就是我們今天的主角時就很重要了。   進到下一個小節前,我們先來整理一下,不論你是不是已經完全懂了,我想拿個東西來比喻 Python 的變數:N次貼。變數就像 N次貼一樣,上面寫著變數的名字,然後我們讓他參考到不同的物件時,就像拿著這個 N次貼到處黏貼一樣,因此只要是黏到( 參考) 不同的東西(物件),就一定是在不同的位置上(記憶體位址)。   在這裡我們也可以看到為什麼 Python 的變數可以一下儲存整數,一下又是字串,因為就如同剛剛所說的,我們終究只是拿著 N次貼在到處黏而已,而這 N次貼上又沒有規定我們一定要黏在什麼東西上 不然就會爆炸什麼的(呃 ,所以我們可以把他黏到各種型態上面都沒問題。 List 的陷阱 (1)-指定「=」   接下來的小節中都會以上面 N次貼的概念來講解。當我們把一個參考到 list 的變數指定給另一個變數時: Lt = [1, 2, 3] Lt2 = Lt   看起來好像是我們把 [1, 2, 3] 複製了一遍,再指定給 Lt2,但實際上只是把寫著「Lt2」的 N次貼也貼到那個 list 上而已,我們可以透過 id() 來驗證。所以當我們對其中一個 list 做變動的話... 就會兩個變數一起被更改! 也 就是踏入黑