溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

從零開始學Python:20課-函數(shù)使用進階

發(fā)布時間:2020-08-10 16:09:27 來源:ITPUB博客 閱讀:149 作者:千鋒Python唐小強 欄目:編程語言

在之前的課程中,我們講到過關于函數(shù)的知識,我們還講到過Python中常用的數(shù)據(jù)類型,這些類型的變量都可以作為函數(shù)的參數(shù)或返回值;通過前幾節(jié)課的學習,我們又知道了寫在類中的函數(shù)通常稱之為方法,它代表了類或者對象可以接收的消息。如果我們把這些知識匯總一下,我們的函數(shù)就可以做更多的事情。

關鍵字參數(shù)

下面是一個判斷傳入的三條邊長能否構成三角形的函數(shù),在調用函數(shù)傳入?yún)?shù)時,我們可以指定參數(shù)名,也可以不指定參數(shù)名,代碼如下所示。

def can_form_triangle(a, b, 
c):

    print(f'a = {a}, b = , c = { c}')
    return a + b > c and b + c > a and a + c > b # 調用函數(shù)傳入?yún)?shù)不指定參數(shù)名按位置對號入座
print(can_form_triangle( 1, 2, 3))
# 調用函數(shù)通過“參數(shù)名=參數(shù)值”的形式按順序傳入?yún)?shù)
print(can_form_triangle(a= 1, b= 2, c= 3))
# 調用函數(shù)通過“參數(shù)名=參數(shù)值”的形式不按順序傳入?yún)?shù)
print(can_form_triangle( c= 3, a= 1, b= 2))

在沒有特殊處理的情況下,函數(shù)的參數(shù)都是 位置參數(shù),也就意味著傳入?yún)?shù)的時候對號入座即可,如上面代碼的第7行所示,傳入的參數(shù)值1、2、3會依次賦值給參數(shù)a、b、c。當然,也可以通過參數(shù)名=參數(shù)值的方式傳入函數(shù)所需的參數(shù),因為指定了參數(shù)名,傳入?yún)?shù)的順序可以進行調整,如上面代碼的第9行和第11行所示。

調用函數(shù)時,如果希望函數(shù)的調用者必須以參數(shù)名=參數(shù)值的方式傳參,可以用 命名關鍵字參數(shù)取代位置參數(shù)。所謂命名關鍵字參數(shù),是在函數(shù)的參數(shù)列表中,寫在*之后的參數(shù),代碼如下所示。



def 
can_form_triangle
(*, a, b, c):

   print( f'a = {a}, b = , c = {c}')
    return a + b > c and b + c > a and a + c > b # TypeError: can_form_triangle() takes 0 positional arguments but 3 were given
# print(is_valid_for_triangle(3, 4, 5))
# 傳參時必須使用“參數(shù)名=參數(shù)值”的方式,位置不重要
print(can_form_triangle(a=3, b=4, c=5))
print(can_form_triangle(c=5, b=4, a=3))

注意:上面的can_form_triangle函數(shù),參數(shù)列表中的*是一個分隔符,*前面的參數(shù)都是位置參數(shù),而*后面的參數(shù)就是命名關鍵字參數(shù)。

我們之前講過在函數(shù)的參數(shù)列表中可以使用 可變參數(shù)*args來接收任意數(shù)量的參數(shù),但是我們需要看看,*args是否能夠接收帶參數(shù)名的參數(shù)。



def 
calc
(*args):

   result = 0
    for arg in args:
       result += arg
    return result print(calc(a= 1, b= 2, c= 3))

執(zhí)行上面的代碼會引發(fā)TypeError錯誤,錯誤消息為calc() got an unexpected keyword argument 'a',由此可見,*args并不能處理帶參數(shù)名的參數(shù)。我們在設計函數(shù)時,如果既不知道調用者會傳入的參數(shù)個數(shù),也不知道調用者會不會指定參數(shù)名,那么同時使用可變參數(shù)和 關鍵字參數(shù)。關鍵字參數(shù)會將傳入的帶參數(shù)名的參數(shù)組裝成一個字典,參數(shù)名就是字典中鍵值對的鍵,而參數(shù)值就是字典中鍵值對的值,代碼如下所示。

def calc(*args, **kwargs):

   result = 0
    for arg in args:
       result += arg
    for value in kwargs.values():
       result += value
    return total print(calc())                  # 0
print(calc( 1, 2, 3))           # 6
print(calc(a= 1, b= 2, c= 3))     # 6
print(calc( 1, 2, c= 3, d= 4))    # 10

提示不帶參數(shù)名的參數(shù)(位置參數(shù))必須出現(xiàn)在帶參數(shù)名的參數(shù)(關鍵字參數(shù))之前,否則將會引發(fā)異常。例如,執(zhí)行calc(1, 2, c=3, d=4, 5)將會引發(fā)SyntaxError錯誤,錯誤消息為positional argument follows keyword argument,翻譯成中文意思是“位置參數(shù)出現(xiàn)在關鍵字參數(shù)之后”。

從零開始學Python:20課-函數(shù)使用進階

高階函數(shù)的用法

在前面幾節(jié)課中,我們講到了面向對象程序設計,在面向對象的世界中,一切皆為對象,所以類和函數(shù)也是對象。函數(shù)的參數(shù)和返回值可以是任意類型的對象,這就意味著 函數(shù)本身也可以作為函數(shù)的參數(shù)或返回值,這就是所謂的 高階函數(shù)。

如果我們希望上面的calc函數(shù)不僅僅可以做多個參數(shù)求和,還可以做多個參數(shù)求乘積甚至更多的二元運算,我們就可以使用高階函數(shù)的方式來改寫上面的代碼,將加法運算從函數(shù)中移除掉,具體的做法如下所示。


def 
calc(
*args, init_value, op, **kwargs):

   result = init_value
    for arg in args:
       result = op(result, arg)
    for value in kwargs.values():
       result = op(result, value)
    return result

注意,上面的函數(shù)增加了兩個參數(shù),其中init_value代表運算的初始值,op代表二元運算函數(shù)。經過改造的calc函數(shù)不僅僅可以實現(xiàn)多個參數(shù)的累加求和,也可以實現(xiàn)多個參數(shù)的累乘運算,代碼如下所示。



def 
add
(x, y):

    return x + y def mul(x, y):
    return x * y print(calc( 1, 2, 3, x= 4, y= 5, init_value= 0, op=add))       # 15
print(calc(1, 2, init_value=1, op=mul, x=3, y=4, z=5))    # 120

通過對高階函數(shù)的運用,calc函數(shù)不再和加法運算耦合,所以靈活性和通用性會變強,這是編程中一種常用的技巧,但是最初學者來說可能會稍微有點難以理解。需要注意的是,將函數(shù)作為參數(shù)和調用函數(shù)是有顯著的區(qū)別的, 調用函數(shù)需要在函數(shù)名后面跟上圓括號,而把函數(shù)作為參數(shù)時只需要函數(shù)名即可。上面的代碼也可以不用定義add和mul函數(shù),因為Python標準庫中的operator模塊提供了代表加法運算的add和代表乘法運算的mul函數(shù),我們直接使用即可,代碼如下所示。


import 
operator


print(calc(init_value=0, op=operator.add, 1 , 2 , 3 , x=4, y=5))       # 15
print(calc(init_value=1, op=operator.mul, 1, 2, x=3, y=4, z=5))    # 120

Python內置函數(shù)中有不少高階函數(shù),我們前面提到過的filter和map函數(shù)就是高階函數(shù),前者可以實現(xiàn)對序列中元素的過濾,后者可以實現(xiàn)對序列中元素的映射,例如我們要去掉一個整數(shù)列表中的奇數(shù),并對所有的偶數(shù)求平方得到一個新的列表,就可以直接使用這兩個函數(shù)來做到,具體的做法是如下所示。



def 
is_even
(num):

    return num % 2 == 0 def square(num):
    return num ** 2 numbers1 = [ 35, 12, 8, 99, 60, 52]
numbers2 = list(map(square, filter(is_even, numbers1)))
print(numbers2)     # [144, 64, 3600, 2704]

當然,要完成上面代碼的功能,也可以使用列表生成式,列表生成式的做法更為簡單優(yōu)雅。


numbers1 
= 
[35, 
12
, 
8
, 
99
, 
60
, 
52
]

numbers2 = [num ** 2 for num in numbers1 if num % 2 == 0 ]
print(numbers2)     # [144, 64, 3600, 2704]

Lambda函數(shù)

在使用高階函數(shù)的時候,如果作為參數(shù)或者返回值的函數(shù)本身非常簡單,一行代碼就能夠完成,那么我們可以使用 Lambda函數(shù)來表示。Python中的Lambda函數(shù)是沒有的名字函數(shù),所以很多人也把它叫做 匿名函數(shù),匿名函數(shù)只能有一行代碼,代碼中的表達式產生的運算結果就是這個匿名函數(shù)的返回值。上面代碼中的is_even和square函數(shù)都只有一行代碼,我們可以用Lambda函數(shù)來替換掉它們,代碼如下所示。


numbers1 
= 
[35, 
12
, 
8
, 
99
, 
60
, 
52
]

numbers2 = list(map(lambda x: x ** 2 , filter(lambda x: x % 2 == 0 , numbers1)))
print(numbers2)     # [144, 64, 3600, 2704]

通過上面的代碼可以看出,定義Lambda函數(shù)的關鍵字是lambda,后面跟函數(shù)的參數(shù),如果有多個參數(shù)用逗號進行分隔;冒號后面的部分就是函數(shù)的執(zhí)行體,通常是一個表達式,表達式的運算結果就是Lambda函數(shù)的返回值,不需要寫return 關鍵字。

如果需要使用加減乘除這種簡單的二元函數(shù),也可以用Lambda函數(shù)來書寫,例如調用上面的calc函數(shù)時,可以通過傳入Lambda函數(shù)來作為op參數(shù)的參數(shù)值。當然,op參數(shù)也可以有默認值,例如我們可以用一個代表加法運算的Lambda函數(shù)來作為op參數(shù)的默認值。


def 
calc(
*args, init_value=
0, op=lambda x, y: x + y, **kwargs):

   result = init_value
    for arg in args:
       result = op(result, arg)
    for value in kwargs.values():
       result = op(result, value)
    return result # 調用calc函數(shù),使用init_value和op的默認值
print(calc(1, 2, 3, x=4, y=5))    # 15
# 調用calc函數(shù),通過lambda函數(shù)給op參數(shù)賦值
print(calc(1, 2, 3, x=4, y=5, init_value=1, op=lambda x, y: x * y))    # 120

提示:注意上面的代碼中的calc函數(shù),它同時使用了可變參數(shù)、關鍵字參數(shù)、命名關鍵字參數(shù),其中命名關鍵字參數(shù)要放在可變參數(shù)和關鍵字參數(shù)之間,傳參時先傳入可變參數(shù),關鍵字參數(shù)和命名關鍵字參數(shù)的先后順序并不重要。

有很多函數(shù)在Python中用一行代碼就能實現(xiàn),我們可以用Lambda函數(shù)來定義這些函數(shù),調用Lambda函數(shù)就跟調用普通函數(shù)一樣,代碼如下所示。


import 
operator, 
functools


# 一行代碼定義求階乘的函數(shù)
fac = lambda num: functools.reduce(operator.mul, range(1, num + 1 ), 1 )
# 一行代碼定義判斷素數(shù)的函數(shù)
is_prime = lambda x: x > 1 and all(map(lambda f: x % f, range(2, int(x ** 0.5 ) + 1 )))

# 調用Lambda函數(shù)
print(fac(10))         # 3628800
print(is_prime(9))    # False

提示1:上面使用的reduce函數(shù)是Python標準庫functools模塊中的函數(shù),它可以實現(xiàn)對數(shù)據(jù)的歸約操作,通常情況下, 過濾(filter)、 映射(map)和 歸約(reduce)是處理數(shù)據(jù)中非常關鍵的三個步驟,而Python的標準庫也提供了對這三個操作的支持。
提示2:上面使用的all函數(shù)是Python內置函數(shù),如果傳入的序列中所有布爾值都是True,all函數(shù)就返回True,否則all函數(shù)就返回False。

簡單的總結

Python中的函數(shù)可以使用可變參數(shù)*args和關鍵字參數(shù)**kwargs來接收任意數(shù)量的參數(shù),而且傳入?yún)?shù)時可以帶上參數(shù)名也可以沒有參數(shù)名,可變參數(shù)會被處理成一個元組,而關鍵字參數(shù)會被處理成一個字典。Python中的函數(shù)也是對象,所以函數(shù)可以作為函數(shù)的參數(shù)和返回值,也就是說,在Python中我們可以使用高階函數(shù)。如果我們要定義的函數(shù)非常簡單,只有一行代碼且不需要名字,可以將函數(shù)寫成Lambda函數(shù)(匿名函數(shù))的形式。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI