『無為則無心』Python函式 — 26、Python函式引數的傳遞方式

繁華似錦Fighting發表於2021-07-12

提示:上一篇文章介紹了Python中函式的基本使用,本篇文章主要說明一下Python函式實參的傳遞方式

1、位置引數

位置引數:呼叫函式時根據函式定義的引數位置來傳遞引數。

def user_info(name, age, gender):
    print(f'您的名字是{name}, 年齡是{age}, 性別是{gender}')

# 呼叫函式
user_info('TOM', 20, '男')

注意:傳遞和定義引數的順序及個數必須一致。就是將對應位置的實參複製給對應位置的形參,第一個實參賦值給第一個形參,第二個實參賦值給第二個形參,以此類推。

示例

def user_info(name, age, gender):
    print(f'您的名字是{name}, 年齡是{age}, 性別是{gender}')

# 1.呼叫函式,傳遞的引數個數不正確
# 結果:TypeError: user_info() missing 1 required positional argument: 'gender'
user_info('TOM', 20)

# 2.呼叫函式,傳遞的引數的順序不正確
# 不會報錯,但輸出的內容完全歧義。
# 結果:您的名字是男, 年齡是TOM, 性別是20
user_info('男', 'TOM', 20)

2、關鍵字引數

可以不按照形參定義的順序去傳遞,而直接根據引數名去傳遞引數。

函式呼叫,通過鍵=值形式加以指定。

可以讓函式更加清晰、容易使用,同時也清除了引數的需求。

def user_info(name, age, gender):
    print(f'您的名字是{name}, 年齡是{age}, 性別是{gender}')

# 正確用法
user_info('Rose', age=20, gender='女')
user_info('小明', gender='男', age=16)

# 位置引數必須寫在關鍵字引數的前面
# 結果:SyntaxError: positional argument follows keyword argument
user_info(gender='男', age=16, "孫悟空")

# 位置引數必須寫在關鍵字引數的前面,同時位置引數的順序也要對應
# 結果:TypeError: user_info() got multiple values for argument 'age'
user_info("孫悟空", '男', age=16)

# 格式正確,但是位置引數的順序不正確,結果也會產生歧義。
# 結果:您的名字是20, 年齡是孫悟空, 性別是男
user_info(20, "孫悟空", gender='男')

"""
總結:定義引數時,位置引數和關鍵字引數,儘量統一使用一種。
"""

注意:

  • 位置引數和關鍵字引數可以混合使用。
  • 函式呼叫時,如果有位置引數時,位置引數必須在關鍵字引數的前面,但關鍵字引數之間不存在先後順序。

3、預設引數(預設引數)

預設引數也叫預設引數,用於定義函式時,為引數提供預設值,呼叫函式時可不傳該預設引數的值。

提示:所有位置引數必須出現在預設引數前(因為預設引數的格式和關鍵字引數格式一樣,所以都要寫在所有引數之後),包括函式定義和呼叫。

def user_info(name, age, gender='男'):
    print(f'您的名字是{name}, 年齡是{age}, 性別是{gender}')

# 沒有為預設引數傳值,表示使用預設值。
user_info('TOM', 20)
# 為預設引數傳值,使用這個值,即修改了預設值。
user_info('Rose', 18, '女')

注意:函式呼叫時,如果為預設引數傳值則修改預設引數值,否則使用這個預設值。

4、不定長引數(可變引數)

不定長引數也叫可變引數。用於不確定呼叫的時候會傳遞多少個引數(不傳參也可以)的場景。此時,可用包裹位置引數,或者包裹關鍵字引數,來進行引數傳遞,會顯得非常方便。

(1)包裹位置傳遞

"""
1.在定義函式時,可以在形參前邊加上一個*,這樣這個形參將會獲取到所有的實參,
它將會將所有的實參儲存到一個元組中
2.*args說明:*一定要寫,args表示形參,可以自定義名稱。
但在實際工作中,一般預設使用*args做為不定長包裹位置傳遞引數的表示,不做修改。
"""
def user_info(*args):
    print(args)


# ('TOM',)
user_info('TOM')
# ('TOM', 18)
user_info('TOM', 18)
# ()
user_info()

注意:傳進的所有引數都會被args變數收集,它會根據傳進的所有的位置實參,合併為一個元組(tuple),args是元組型別,這就是包裹位置傳遞。(這個過程也叫裝包或者組包)

小應用

# 定義一個函式,可以求任意個數字的和
def sum(*nums):
    # 定義一個變數,來儲存結果
    result = 0
    # 遍歷元組,並將元組中的數進行累加
    for n in nums:
        result += n
    print(result)


sum(123, 456, 789, 10, 20, 30, 40)

注意事項

# 注意事項1
# 帶星號的形參只能有一個
# 帶星號的引數,可以和其他引數配合使用
# 第一個引數給a,第二個引數給b,剩下的都儲存到c的元組中
def fn2(a, b, *c):
    print('a =', a)
    print('b =', b)
    print('c =', c)

fn2(1, 2, 3, 4, 5)
"""
輸出結果:
a = 1
b = 2
c = (3, 4, 5)
"""

# 注意事項2
# 可變引數不是必須寫在最後,但是注意,
# 帶*的引數後的所有引數,必須以關鍵字引數的形式傳遞
# 第一個引數給a,剩下的位置引數給b的元組,c必須使用關鍵字引數
def fn2(a, *b, c):
    print('a =', a)
    print('b =', b)
    print('c =', c)
# TypeError: fn2() missing 1 required keyword-only argument: 'c'
# fn2(1, 2, 3, 4, 5) # 報錯
fn2(1, 2, 3, 4, c=5)
"""
輸出結果:
a = 1
b = (2, 3, 4)
c = 5
"""

# 注意事項3
# 所有的位置引數都給a,b和c必須使用關鍵字引數,
# 且必須寫在最後。
def fn2(*a, b, c):
    print('a =', a)
    print('b =', b)
    print('c =', c)

fn2(1, 2, 3, b=4, c=5)
"""
輸出結果:
a = (1, 2, 3)
b = 4
c = 5
"""

# 注意事項4
# 如果在形參的開頭直接寫一個*,
# 則要求我們的所有的引數必須以關鍵字引數的形式傳遞。
def fn2(*, a, b, c):
    print('a =', a)
    print('b =', b)
    print('c =', c)

fn2(a=3, b=4, c=5)
"""
輸出結果:
a = 3
b = 4
c = 5
"""

# 注意事項5
# *形參只能接收位置引數,而不能接收關鍵字引數
def fn3(*a):
    print('a =', a)
# TypeError: fn3() got an unexpected keyword argument 'a'
fn3(a=3, b=4, c=5)

(2)包裹關鍵字傳遞

"""
**kwargs說明:**一定要寫,args表示形參,可以自定義名稱。
但在實際工作中,一般預設使用**kwargs做為不定長包裹關鍵字傳遞引數的表示,不做修改。
"""
# 收集所有關鍵字引數,它會將這些引數統一儲存到一個字典中返回。
# 字典的key就是引數的名字,字典的value就是引數的值
def user_info(**kwargs):
    print(kwargs)

# {'name': 'TOM', 'age': 18, 'id': 110}
# 關鍵字不能加引號,應為關鍵字對應的是形參。
user_info(name='TOM', age=18, id=110)


# 包裹關鍵字傳遞只能有一個,並且必須寫在所有引數的最後
# 實參可以不按順序傳遞
def fn3(b, c, **a):
    print('a =', a, type(a))
    print('b =', b)
    print('c =', c)

fn3(e=10, b=1, d=2, c=3, f=20)
"""
輸出結果:
a = {'e': 10, 'd': 2, 'f': 20} <class 'dict'>
b = 1
c = 3
"""

綜上:無論是包裹位置傳遞還是包裹關鍵字傳遞,都是一個組包的過程(組包簡單的說,就是收集分散的引數,返回一個整體資料)。關於組包對應就是拆包,在之前的文章【Python元組】中有介紹,可以對應的檢視。

5、位置引數、預設引數、可變引數的混合使用

引數定義與呼叫順序的基本原則是:位置引數,預設引數,包裹位置,包裹關鍵字。(定義和呼叫都應遵循)

提示:預設引數與包裹位置引數可以根據需求進行位置調換。

def user_info(name, age, sex=1, *args, **kargs):
    print(name, age, sex, args, kargs)


user_info('TOM', 18, 2, 'music', 'sport', id=101)

# 輸出結果
# TOM 18 2 ('music', 'sport') {'id': 101}

6、擴充:引數解包

關於組包對應就是拆包,在之前的文章【Python元組】中有介紹,可以對應的檢視。

(1)通過包裹位置對一個元組進行解包

操作方式:使用*號。

如下示例:

# 引數的解包(拆包)
def fn4(a, b, c):
    print('a =', a)
    print('b =', b)
    print('c =', c)


# 建立一個元組
t = (10, 20, 30)

# 我們直接傳遞t,會報錯
# TypeError: fn4() missing 2 required positional arguments: 'b' and 'c'
# fn4(t)

# 而以前我們是這樣拆包的
# fn4(t[0], t[1], t[2])
"""
輸出結果:
a = 10
b = 20
c = 30
"""

# *號拆包
# 傳遞實參時,也可以在序列型別的引數前新增星號,
# #這樣他會自動將序列中的元素依次作為引數傳遞。
# 這裡要求序列中元素的個數必須和形參的個數的一致
fn4(*t)
"""
輸出結果:
a = 10
b = 20
c = 30
"""

(2)通過包裹關鍵字對一個字典進行解包

操作方式:使用**號。

如下示例:

# 引數的解包(拆包)
def fn4(a, b, c):
    print('a =', a)
    print('b =', b)
    print('c =', c)


# 建立一個字典
d = {'a': 100, 'b': 200, 'c': 300}
# 通過 **來對一個字典進行解包操作
# 字典中的key要和函式的形參一一對應。
fn4(**d)
"""
輸出結果:
a = 100
b = 200
c = 300
"""

相關文章