路遙知碼力,日久練成精-只要在程式之路鑽研的夠深,便能夠充分發揮程式碼的力量; 練習的日子夠久,便能夠練成寫出精簡代碼的能力。
至今為止我們認識了許多python的內建函數,
你對於自定義函數了解多少呢?
先看昨天的課後習題,
(還沒看過題目的朋友歡迎點昨日題目傳送門)
首先昨天有教過如何將一個矩陣右旋轉90度:
def rotateRight(arr):
A = arr[::-1] #利用切片上下翻轉
return list(map(list,(zip(*A)))) #行列互換,再利用map函數將zip內的元組轉列表
而例題要我們向左旋轉90度。
或許你不知道直接的做法,
但或許你日常生活經驗會知道,
把一張照片向右旋轉三次即得到向左旋轉一次的效果,
因此可以這樣寫:
def rotateLeft(arr):
for i in range(3):
arr = list(map(list,(zip(*arr[::-1]))))
return arr
但也許我們不是很滿意參考解法一,
因為真的這樣做的話,
向左旋轉豈不比向右旋轉還慢三倍嗎?
有沒有更直接的方法呢?
這邊我們觀察到,
首先先把圖片水平翻轉,再做行列互換,
即可達到向左旋轉的效果的,
給個示意圖:
123 (水平翻轉) 321 (行列互換) 369
456 => 654 => 258
789 987 147
程式如下:
def rotateLeft(arr):
A = [a[::-1] for a in arr] #水平翻轉
return list(map(list,(zip(*A)))) #行列互換,再利用map函數將zip內的元組轉列表
回到本日主題,你知道幾種python函數的定義方式呢?
這是平時我們最常見的定義方式,
例如我們很普通的寫一個計算x乘以y的函數:
def mul(x,y):
return x*y
那麼x,y便稱為「位置參數」,
因為當你調用函數時,
傳入的兩個值會按照順序賦值給x,y。
例如:
>>> mul(3, 4)
12
調用 mul(3, 4)
時,表示x=3, y=4。
默認參數可以簡化我們調用函數,
比如說假設我們的程式需要常常計算x*2
的值好了,
只有少數特例才會需要計算其它x*y
的值,
我們需要每次都調用 mul(x, 2)
就顯得有點麻煩,
這時便可以將函數改寫成這樣:
def mul(x, y=2):
return x*y
表示y這個參數默認為2,
未來調用函數時,
不論是傳入一個參數或兩個參數都可以囉。
>>> mul(5)
10
>>> mul(5,3)
15
注意同時用位置參數及默認參數時,
默認參數一定要放在位置參數之後。
我們也可以有多個默認參數,例如:
def mulThenPlus(x, y=2, z=1):
return x*y+z
如果我們只想傳入x
和z
這兩個參數維持默認y=2
怎麼辦?
這時我們就可以給參數名字來傳值:
print(mulThenPlus(2,z=6))
結果為10
(2*2+6的結果)。
我們來思考個問題,
如果我們想要定義一個函數,
計算兩個數的乘積,
你會怎麼寫呢?
很簡單,相信你很容易便寫的出來:
def mul2(x, y):
return x*y
好,那如果你想定義一個函數,
計算三個數的乘積,
你可能會再多加一個參數進去,
def mul3(x, y, z):
return x*y*z
那如果我想計算十個數的乘積呢?
你總不可能無限制的一直把參數寫下去。
還好python 裡面用個好用的特性,
可以接收任意數量的參數(甚至是零個參數),
我稱之為「單星語法」,
(補充說明:
若你從前學過C/C++,
可能會對這樣的語法困惑半天,
覺得python裡竟然也有*
,是指標嗎?
事實上python的*
跟指標是沒關係的哦)
舉例來說:
def test(*args):
for a in args:
print(a)
test(1,'A',3)
結果印出: 1
A
3
在參數的定義前面加一顆*
,
即可把傳入的參數打包成一個元組(tuple)傳進函數中。
因此,實作接受任意參數數量的數字乘積則可以這樣改寫:
def mul(*args):
result = 1
for a in args:
result *= a
return result
實際測試調用函數看看:
>>> mul(5,6,3)
90
那如果你原本的資料是個列表或元組怎麼辦呢?
譬如:
nums = [5,6,3]
想要把nums裡的數字當做參數傳進函數裡。
我們在昨天教zip函數時其實已經示範過一次了,
方法還是在列表或元組前面加一顆*
,
它會把nums裡的數拆解開參做參數5, 6, 3
傳進函數中。
範例如下:
>>> nums = [5,6,3]
>>> mul(*nums)
90
相較於「單星語法」是可以傳入任意數量不具名字的參數,
「雙星語法」便是可以傳入任意數量帶有名字的參數了,
在參數的定義前面加兩顆*
,
即可把傳入的參數打包成一個字典(dict)傳進函數中。
舉例如下:
def test(**kargs):
for n in kargs:
print(f'kargs: {n} -> {kargs[n]}')
# 底下測試結果
D = {'apple': '蘋果', 'banana': '香蕉'}
test(**D) #若要直接傳入字典,也是用兩個*
結果會印出 kargs: apple -> 蘋果
kargs: banana -> 香蕉
亦可直接打關鍵字傳參數:
test(apple='蘋果', banana='香蕉')
結果一樣可以印出 kargs: apple -> 蘋果
kargs: banana -> 香蕉
好了,介紹完python的自定義函數參數了,
python在自定義函數是非常自由的,
上述提到的這些參數都是可以混著使用的,
但是在定義參數上,
必須遵守以下的順序:
必選參數(位置參數)、默認參數、可變參數、關鍵字參數。
舉例來說:
def test(a, b, c=0, *args, **kargs):
print(a)
print(b)
print("c=", c)
for a in args:
print(a)
for n in kargs:
print(f'kargs: {n} -> {kargs[n]}')
test(10,20,30,40,apple='蘋果', banana='香蕉')
會印出10
20
c= 30
40
kargs: apple -> 蘋果
kargs: banana -> 香蕉
雖然python在函數定義上如此自由,
但其實是不建議全部一起混著用啦,
這樣會讓函數變的不是那麼好理解。
那麼今天就先學到這裡啦。
由於這一個寫法可能沒是像前述函數參數定義那麼常見,
但不失為一個python經典例子,
放在課後學習供有興趣的人看。
想像我們現在要定義多個功能相似的函數,
例如函數1用來計算1的倍數,
函數2用來計算2的倍數,…
函數5用來計算5的倍數。
這時可以怎麼做呢?
一個純樸的方式就是乖乖的寫五次函數定義,例如:
def func1(x):
return x
def func2(x):
return x*2
def func3(x):
return x*3
def func4(x):
return x*4
def func5(x):
return x*5
於是我們便定義了五個函數,
但是這樣重複的程式實在很多又不簡潔,
於是有人會想到說,我們不是在Day14有學過叫匿名函數的東西嗎?
於是他用匿名函數lambda
加上列表生成式的效果,
這不就生成了五個函數嗎?如下:
mulFuncs = [lambda x: x*i for i in range(1,6)]
那麼問題來了,
你覺得下面這段程式碼的執行結果會印出什麼?
(你可以試著真的執行程式看看,可能會讓你驚訝)
mulFuncs = [lambda x: x*i for i in range(1,6)]
for func in mulFuncs:
print(func(2))
歡迎於留言區討論想法。