本次我要給大家分享一些更加高級和不常見的函數定義與使用模式。
涉及到的內容包括默認參數、任意數量參數、強制關鍵字 參數、注解和閉包等。
* 表達式
我們知道在定義函數的時候,用 * 表達式來收集所有未指明的位置參數。
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_value是object()的是個實例, 這樣可以判斷變量b的值和_no_value的值是否是同一個對象來判斷用戶是否傳入了值。
匿名函數
在匿名函數中使用了一個變量的值,這會是很有意思的一件事。
li = [lambda n=30: n for n in range(10)]
現在你回答下面幾個問題
-
li[0]是什么類型的對象?
li是一個列表,其中的元素都是匿名函數
-
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ù)...