1. 程式人生 > 實用技巧 >4. 函式引數

4. 函式引數

1、預設引數

def power(x, n = 2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

呼叫

>>> power(5)
25
>>> power(5, 2)
25

設定預設引數的注意點:

  • 必選引數在前,預設引數在後,否則Python會報錯
  • 當函式有多個引數時,變化大的引數放在前面,變化小的引數放在後面,變化小的引數可以作為預設引數

預設引數的坑演示:

# 先定義一個函式,傳入一個list,新增一個END再返回
def add_end(L = []):
    L.aappend('END')
    return L
# 正常呼叫結果似乎不錯
>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']
# 開始使用預設引數,開始結果正確
>>> add_end()
['END']

# 但再次呼叫add_end()時
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

預設引數是[],但函式似乎每次都‘’記住了‘上次新增的’END‘的list。

原因:Python函式在定義的時候,預設引數L的值就被計算出來了,即[],因為預設引數L也是一個變數,它指向物件[],每次呼叫該函式,如果改變了L的內容,則下次呼叫時,預設引數的內容就變了,不再是函式定義時的[]了。

預設引數必須指向不變物件!

要修改上面的例子,我們可以用None這個不變物件來實現:

def add_end(L=None):
	if L is None:
		L = []
	L.append('END')
	return L

# 呼叫
>>> add_end()
['END']
>>> add_end()
['END']

為什麼要設計str、None這樣的不變物件呢?因為不變物件一旦建立,物件內部的資料就不能修改,這樣就減少了由於修改資料導致的錯誤。

2、可變引數

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

>>> calc(1, 2, 3)
14
>>> calc(1, 3, 5, 7)
84
>>> calc()
0

定義可變引數和定義一個list或tuple引數相比,僅僅在引數前面加了一個*號。在函式內部,引數numbers接收到的是一個tuple

,因此,函式程式碼完全不變。

如果已經有一個list或者tuple,要呼叫一個可變引數怎麼辦?

# 法一
>>> nums = [1, 2, 3]
>>> calc(nums[0], nums[1], nums[2])
14

# 法二
>>> nums = [1, 2, 3]
>>> calc(*nums)
14

Python允許在list或tuple前面加一個*號,把list或tuple的元素變成可變引數傳進去。

3、關鍵字引數

關鍵字引數允許你傳入0個或任意個含引數名的引數,這些關鍵字引數在函式內部自動組裝為一個dict。

def person(name, gao, **kw):
	print('name:', name, 'age:', age, 'other:', kw)

# 只傳入必選引數
>>> person('Michel', 30)
name:Michel age:30 other:{}
# 傳入任意個數的關鍵字引數
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

和可變引數類似,也可以先組裝出一個dict,然後,把該dict轉換為關鍵字引數傳進去

>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

**extra表示把extra這個dict的所有key-value用關鍵字引數傳入到函式的**kw引數,kw將獲得一個dict,注意kw獲得的dict是extra的一份拷貝,對kw的改動不會影響到函式外的extra。

4、命名關鍵字引數

對於關鍵字引數,函式的呼叫者可以傳入任意不受限制的關鍵字引數。至於到底傳入了哪些,就需要在函式內部通過kw檢查。如果要限制關鍵字引數的名字,就可以用命名關鍵字引數

例如,只接收city和job作為關鍵字引數。這種方式定義的函式如下:

def person(name, age, *, city, job):
    print(name, age, city, job)

和關鍵字引數**kw不同,命名關鍵字引數需要一個特殊分隔符*,*後面的引數被視為命名關鍵字引數

>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

命名關鍵字引數必須傳入引數名,這和位置引數不同。如果沒有傳入引數名,呼叫將報錯;原因:呼叫命名引數缺少引數名時,Python直譯器把函式引數均視為位置引數。

# 命名關鍵字引數可以有預設值,從而簡化呼叫:
def person(name, age, *, city='Beijing', job):
	print(name, age, city, job)

5、組合引數

在Python中定義函式,可以用必選引數預設引數可變引數關鍵字引數命名關鍵字引數,這5種引數都可以組合使用。

引數定義的順序必須是:必選引數、預設引數、可變引數/命名關鍵字引數和關鍵字引數。

def f1(a, b, c=0, *args, **kw):
	print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}    
def f2(a, b, c=0, *, d, **kw):
	print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
    
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

對於任意函式,都可以通過類似func(*args, **kw)的形式呼叫它,無論它的引數是如何定義的。