Python3 函數的更高級的話題(一)

本次我要給大家分享一些更加高級和不常見的函數定義與使用模式。

涉及到的內容包括默認參數、任意數量參數、強制關鍵字 參數、注解和閉包等。

* 表達式

我們知道在定義函數的時候,用 * 表達式來收集所有未指明的位置參數。

def avg(first, *rest):
    """求平均值"""
    return (first + sum(rest)) / (1 + len(rest))
    
# 簡單使用
avg(1, 2)  # 1.5 
avg(1, 2, 3, 4)  # 2.5

當然在調用函數的時候,也可以使用 * 表達式來把一個序列類型數據中的元素一一解開。

def show_args(*args):
    print(args)

show_args(['a','b'])  # (['a', 'b'],)
show_args(*['a','b'])  # ('a', 'b')

print() 函數的另外一種用法

print(['a','b'], sep='\n') 
# ['a', 'b']

print(*['a','b'], sep='\n')
# a
# b

print(*'ab', sep='\n')
# a
# b

其實 print() 函數的原型是這樣定義的

def print(self, *args, sep=' ', end='\n', file=None):
    pass

print() 函數中的 sep 關鍵字參數定義的是當打印多個參數時,它們中間的分隔符是什么

** 表達式

def foo(**kwargs):
    print(kwargs)  # 是一個字典

一個 * 參數只能出現在函數定義中最后一個位置參數的后面,而 ** 參數只能出現在最后一個參數都位置。
有一點要注意的是,在 * 參數后面其實還可以定義其他參數。下面就會用到。

在函數中實現強制關鍵字參數

有的時候你希望在調用函數的時候,必須用關鍵字參數,因為這樣更易懂。
可以將要限定的強制關鍵字參數放到某個 *參數 或者單個 * 后面就能達到這種效果。
像下面這樣。

def query_keyword(max_file, *, servers):
    pass

query_keyword(65535, servers=False)  # ok
query_keyword(65535)  # TypeError

使用函數注解

函數注解是為了讓看這個函數源碼的人更能清楚參數的類型和用法

 def add(x:int, y:int) -> int: 
     return x + y

python 解釋器不會對這些注解添加任何的語義。它們不會被類型檢查,運行時跟 沒有加注解之前的效果也沒有任何差距。

這些注解存儲在函數的 __annotations__ 屬性中。
print(add.__annotations__)

感受 return 的強大

def foo():
    return '千鋒', 8, 1000000

name, *nums = foo()

print(name)  # 千鋒
print(nums)  # [8, 1000]

其實在函數返回之前,先創(chuàng)建了一個元組,之后的賦值就是我們之前講的元組解包

在定義函數的默認參數時,不要用可變類型的數據

假如你的確需要一個默認參數是一個可邊類型的數據(比如列表)
可以把默認參數的值先定義為 None

def foo(a, b=None): 
    if b is None:
        b = []

驗證同一性

假如你想在一個函數中判斷使用者有沒有給一個參數傳參,你可能想到這樣:

def spam(a, b=None)
    if not b:  # 判斷是否是 False
        print("用戶沒有傳遞變量")

這樣顯然會有問題的,因為對于 python 來說, 長度為 0 的字符串 ''、列表 []、元組 ()、字典 {} 都會認為是 False, 并且數字 0 和 布爾值的 False 都認為是假的。
也就是說用戶傳入這些參數是屬于合法的參數。

解決辦法:

_no_value = object()
def spam(a, b=_no_value): 
    if b is _no_value:
        print("用戶沒有傳遞變量")

_no_valueobject() 的是個實例, 這樣可以判斷變量 b 的值和 _no_value 的值是否是同一個對象來判斷用戶是否傳入了值。

匿名函數

在匿名函數中使用了一個變量的值,這會是很有意思的一件事。

li = [lambda n=30: n for n in range(10)]

現在你回答下面幾個問題

  1. li[0] 是什么類型的對象?

li 是一個列表,其中的元素都是匿名函數

  1. li[0]()li[1]() 分別都是什么值?

其實 li[0]()li[1]() 的值都是 9

lambda 表達式中的 n 是一個自由變量,是在運行時給其綁定值,而不是在定義時就給其綁定值,這跟函數的默認值參數定義是不同的。
因此,在調用這個 lambda 表達式的時候,n 的值是執(zhí)行時的值。
還有要考慮到, 在 Python 中只有在函數中定義的變量才是局部變量,其他都是全局的變量。 for 循環(huán)到最后 n 的值被綁定為 9, 所以 li 所用函數中的 n 的值在運行時都是 9。

現在對 lambda 函數稍作修改就會有不一樣的效果

funcs = [lambda n=n: n for n in range(10)]
funcs[0]()  # 0
funcs[1]()  # 1

等號左邊的 n 是函數的形參,右邊是函數的實參,實參也就是迭代的變量的值。
這里利用默認參數,就可以在定義函數時,把值綁定給變量。

未完, 待續(xù)...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容