草根學Python(六) 函數(shù)

前言

前天創(chuàng)了個 Python 微信討論群,以為沒人進的,哈哈,想不到還真有小伙伴進群學習討論。如果想進群,可以加我微信: androidwed ,拉進群,就不貼微信群二維碼了,一是會失效,二影響文章。

目錄

草根學Python(六) 函數(shù)

一、Python 自定義函數(shù)的基本步驟

函數(shù)是組織好的,可重復使用的,用來實現(xiàn)單一,或相關聯(lián)功能的代碼段。

自定義函數(shù),基本有以下規(guī)則步驟:

  • 函數(shù)代碼塊以 def 關鍵詞開頭,后接函數(shù)標識符名稱和圓括號()
  • 任何傳入?yún)?shù)和自變量必須放在圓括號中間。圓括號之間可以用于定義參數(shù)
  • 函數(shù)的第一行語句可以選擇性地使用文檔字符串(用于存放函數(shù)說明)
  • 函數(shù)內容以冒號起始,并且縮進
  • return [表達式] 結束函數(shù),選擇性地返回一個值給調用方。不帶表達式的 return 相當于返回 None。

語法示例:

def functionname( parameters ):
   "函數(shù)_文檔字符串"
   function_suite
   return [expression]

實例:

  1. def 定義一個函數(shù),給定一個函數(shù)名 sum
  2. 聲明兩個參數(shù) num1 和 num2
  3. 函數(shù)的第一行語句進行函數(shù)說明:兩數(shù)之和
  4. 最終 return 語句結束函數(shù),并返回兩數(shù)之和
def sum(num1,num2):
    "兩數(shù)之和"
    return num1+num2

# 調用函數(shù)
print(sum(5,6))

輸出結果:

11

二、函數(shù)傳值問題

先看一個例子:

# -*- coding: UTF-8 -*-
def chagne_number( b ):
    b = 1000

b = 1
chagne_number(b)
print( b ) 

最后輸出的結果為:

1

這里可能有些人會有疑問,為啥不是通過函數(shù)chagne_number更改了 b
的值嗎?為啥沒有變化,輸出的結果還是 1 ,這個問題很多編程語言都會講到,原理解釋也是差不多的。

這里主要是函數(shù)參數(shù)的傳遞中,傳遞的是類型對象,之前也介紹了 Python 中基本的數(shù)據(jù)類型等。而這些類型對象可以分為可更改類型和不可更改的類型

在 Python 中,字符串,整形,浮點型,tuple 是不可更改的對象,而 list , dict 等是可以更改的對象。

例如:

不可更改的類型:變量賦值 a = 1,其實就是生成一個整形對象 1 ,然后變量 a 指向 1,當 a = 1000 其實就是再生成一個整形對象 1000,然后改變 a 的指向,不再指向整形對象 1 ,而是指向 1000,最后 1 會被丟棄

可更改的類型:變量賦值 a = [1,2,3,4,5,6] ,就是生成一個對象 list ,list 里面有 6 個元素,而變量 a 指向 list ,a[2] = 5則是將 list a 的第三個元素值更改,這里跟上面是不同的,并不是將 a 重新指向,而是直接修改 list 中的元素值。

指向問題

這也將影響到函數(shù)中參數(shù)的傳遞了:

不可更改的類型:類似 c++ 的值傳遞,如 整數(shù)、字符串、元組。如fun(a),傳遞的只是 a 的值,沒有影響 a 對象本身。比如在 fun(a)內部修改 a 的值,只是修改另一個復制的對象,不會影響 a 本身。

可更改的類型:類似 c++ 的引用傳遞,如 列表,字典。如 fun(a),則是將 a 真正的傳過去,修改后 fun 外部的 a 也會受影響

因此,在一開始的例子中,b = 1,創(chuàng)建了一個整形對象 1 ,變量 b 指向了這個對象,然后通過函數(shù) chagne_number 時,按傳值的方式復制了變量 b ,傳遞的只是 b 的值,并沒有影響到 b 的本身。具體可以看下修改后的實例,通過打印的結果更好的理解。

# -*- coding: UTF-8 -*-
def chagne_number( b ):
    print('函數(shù)中一開始 b 的值:{}' .format( b ) )
    b = 1000
    print('函數(shù)中 b 賦值后的值:{}' .format( b ) )
    

b = 1
chagne_number( b )
print( '最后輸出 b 的值:{}' .format( b )  )


打印的結果:

函數(shù)中一開始 b 的值:1
函數(shù)中 b 賦值后的值:1000
最后輸出 b 的值:1

當然,如果參數(shù)中的是可更改的類型,那么調用了這個函數(shù)后,原來的值也會被更改,具體實例如下:

# -*- coding: UTF-8 -*-

def chagne_list( b ):
    print('函數(shù)中一開始 b 的值:{}' .format( b ) )
    b.append(1000)
    print('函數(shù)中 b 賦值后的值:{}' .format( b ) )
    

b = [1,2,3,4,5]
chagne_list( b )
print( '最后輸出 b 的值:{}' .format( b )  )

輸出的結果:

函數(shù)中一開始 b 的值:[1, 2, 3, 4, 5]
函數(shù)中 b 賦值后的值:[1, 2, 3, 4, 5, 1000]
最后輸出 b 的值:[1, 2, 3, 4, 5, 1000]

三、函數(shù)返回值

通過上面的學習,可以知道通過 return [表達式] 語句用于退出函數(shù),選擇性地向調用方返回一個表達式。不帶參數(shù)值的 return 語句返回 None。

具體示例:

# -*- coding: UTF-8 -*-

def sum(num1,num2):
    # 兩數(shù)之和
    if not (isinstance (num1,(int ,float)) or isinstance (num2,(int ,float))):
        raise TypeError('參數(shù)類型錯誤')
    return num1+num2

print(sum(1,2))

返回結果:

3

這個示例,還通過內置函數(shù)isinstance()進行數(shù)據(jù)類型檢查,檢查調用函數(shù)時參數(shù)是否是整形和浮點型。如果參數(shù)類型不對,會報錯,提示 參數(shù)類型錯誤,如圖:

檢查函數(shù)參數(shù)是否正確

當然,函數(shù)也可以返回多個值,具體實例如下:

# -*- coding: UTF-8 -*-

def  division ( num1, num2 ):
    # 求商與余數(shù)
         a = num1 % num2
         b = (num1-a) / num2
         return b , a 

num1 , num2 = division(9,4)
tuple1 = division(9,4)

print (num1,num2)
print (tuple1)

輸出的值:

2.0 1
(2.0, 1)

認真觀察就可以發(fā)現(xiàn),盡管從第一個輸出值來看,返回了多個值,實際上是先創(chuàng)建了一個元組然后返回的。回憶一下,元組是可以直接用逗號來創(chuàng)建的,觀察例子中的 ruturn ,可以發(fā)現(xiàn)實際上我們使用的是逗號來生成一個元組。

四、函數(shù)的參數(shù)

1、默認值參數(shù)

有時候,我們自定義的函數(shù)中,如果調用的時候沒有設置參數(shù),需要給個默認值,這時候就需要用到默認值參數(shù)了。

# -*- coding: UTF-8 -*-

def print_user_info( name , age , sex = '男' ):
    # 打印用戶信息
    print('昵稱:{}'.format(name) , end = ' ')
    print('年齡:{}'.format(age) , end = ' ')
    print('性別:{}'.format(sex))
    return;

# 調用 print_user_info 函數(shù)

print_user_info( '兩點水' , 18 , '女')
print_user_info( '三點水' , 25 )

輸出結果:

昵稱:兩點水 年齡:18 性別:女
昵稱:三點水 年齡:25 性別:男

可以看到,當你設置了默認參數(shù)的時候,在調用函數(shù)的時候,不傳該參數(shù),就會使用默認值。但是這里需要注意的一點是:只有在形參表末尾的那些參數(shù)可以有默認參數(shù)值,也就是說你不能在聲明函數(shù)形參的時候,先聲明有默認值的形參而后聲明沒有默認值的形參。這是因為賦給形參的值是根據(jù)位置而賦值的。例如,def func(a, b=1) 是有效的,但是 def func(a=1, b) 是 無效 的。

默認值參數(shù)就這樣結束了嗎?還沒有的,細想一下,如果參數(shù)中是一個可修改的容器比如一個 lsit (列表)或者 dict (字典),那么我們使用什么來作為默認值呢?我們可以使用 None 作為默認值。就像下面這個例子一樣:

# 如果 b 是一個 list ,可以使用 None 作為默認值
def print_info( a , b = None ):
    if b is None :
        b=[]
    return;

認真看下例子,會不會有這樣的疑問呢?在參數(shù)中我們直接 b=[] 不就行了嗎?也就是寫成下面這個樣子:

def print_info( a , b = [] ):
    return;

對不對呢?運行一下也沒發(fā)現(xiàn)錯誤啊,可以這樣寫嗎?這里需要特別注意的一點:默認參數(shù)的值是不可變的對象,比如None、True、False、數(shù)字或字符串,如果你像上面的那樣操作,當默認值在其他地方被修改后你將會遇到各種麻煩。這些修改會影響到下次調用這個函數(shù)時的默認值。

示例如下:

# -*- coding: UTF-8 -*-

def print_info( a , b = [] ):
    print(b)
    return b ;

result = print_info(1)

result.append('error')

print_info(2)

輸出的結果:

[]
['error']

認真觀察,你會發(fā)現(xiàn)第二次輸出的值根本不是你想要的,因此切忌不能這樣操作。

還有一點,有時候我就是不想要默認值啊,只是想單單判斷默認參數(shù)有沒有值傳遞進來,那該怎么辦?我們可以這樣做:

_no_value =object()

def print_info( a , b = _no_value ):
    if b is _no_value :
        print('b 沒有賦值')
    return;

這里的 object 是python中所有類的基類。 你可以創(chuàng)建 object 類的實例,但是這些實例沒什么實際用處,因為它并沒有任何有用的方法, 也沒有任何實例數(shù)據(jù)(因為它沒有任何的實例字典,你甚至都不能設置任何屬性值)。 你唯一能做的就是測試同一性。也正好利用這個特性,來判斷是否有值輸入。

2、關鍵字參數(shù)

在 Python 中,可以通過參數(shù)名來給函數(shù)傳遞參數(shù),而不用關心參數(shù)列表定義時的順序,這被稱之為關鍵字參數(shù)。使用關鍵參數(shù)有兩個優(yōu)勢 :

一、由于我們不必擔心參數(shù)的順序,使用函數(shù)變得更加簡單了。

二、假設其他參數(shù)都有默認值,我們可以只給我們想要的那些參數(shù)賦值

# -*- coding: UTF-8 -*-

def print_user_info( name ,  age  , sex = '男' ):
    # 打印用戶信息
    print('昵稱:{}'.format(name) , end = ' ')
    print('年齡:{}'.format(age) , end = ' ')
    print('性別:{}'.format(sex))
    return;

# 調用 print_user_info 函數(shù)

print_user_info( name = '兩點水' ,age = 18 , sex = '女')
print_user_info( name = '兩點水' ,sex = '女', age = 18 )

輸出的值:

昵稱:兩點水 年齡:18 性別:女
昵稱:兩點水 年齡:18 性別:女

3、不定長參數(shù)

有時我們在設計函數(shù)接口的時候,可會需要可變長的參數(shù)。也就是說,我們事先無法確定傳入的參數(shù)個數(shù)。Python 提供了一種元組的方式來接受沒有直接定義的參數(shù)。這種方式在參數(shù)前邊加星號 * 。如果在函數(shù)調用時沒有指定參數(shù),它就是一個空元組。我們也可以不向函數(shù)傳遞未命名的變量。

例如:

# -*- coding: UTF-8 -*-

def print_user_info( name ,  age  , sex = '男' , * hobby):
    # 打印用戶信息
    print('昵稱:{}'.format(name) , end = ' ')
    print('年齡:{}'.format(age) , end = ' ')
    print('性別:{}'.format(sex) ,end = ' ' )
    print('愛好:{}'.format(hobby))
    return;

# 調用 print_user_info 函數(shù)
print_user_info( '兩點水' ,18 , '女', '打籃球','打羽毛球','跑步')

輸出的結果:

昵稱:兩點水 年齡:18 性別:女 愛好:('打籃球', '打羽毛球', '跑步')

通過輸出的結果可以知道,*hobby是可變參數(shù),且 hobby其實就是一個 tuple (元祖)

可變長參數(shù)也支持關鍵參數(shù),沒有被定義的關鍵參數(shù)會被放到一個字典里。這種方式即是在參數(shù)前邊加 **,更改上面的示例如下:

# -*- coding: UTF-8 -*-

def print_user_info( name ,  age  , sex = '男' , ** hobby ):
    # 打印用戶信息
    print('昵稱:{}'.format(name) , end = ' ')
    print('年齡:{}'.format(age) , end = ' ')
    print('性別:{}'.format(sex) ,end = ' ' )
    print('愛好:{}'.format(hobby))
    return;

# 調用 print_user_info 函數(shù)
print_user_info( name = '兩點水' , age = 18 , sex = '女', hobby = ('打籃球','打羽毛球','跑步'))

輸出的結果:

昵稱:兩點水 年齡:18 性別:女 愛好:{'hobby': ('打籃球', '打羽毛球', '跑步')}   

通過對比上面的例子和這個例子,可以知道,*hobby是可變參數(shù),且 hobby其實就是一個 tuple (元祖),**hobby是關鍵字參數(shù),且 hobby 就是一個 dict (字典)

4、只接受關鍵字參數(shù)

關鍵字參數(shù)使用起來簡單,不容易參數(shù)出錯,那么有些時候,我們定義的函數(shù)希望某些參數(shù)強制使用關鍵字參數(shù)傳遞,這時候該怎么辦呢?

將強制關鍵字參數(shù)放到某個*參數(shù)或者單個*后面就能達到這種效果,比如:

# -*- coding: UTF-8 -*-

def print_user_info( name , *, age  , sex = '男' ):
    # 打印用戶信息
    print('昵稱:{}'.format(name) , end = ' ')
    print('年齡:{}'.format(age) , end = ' ')
    print('性別:{}'.format(sex))
    return;

# 調用 print_user_info 函數(shù)
print_user_info( name = '兩點水' ,age = 18 , sex = '女' )

# 這種寫法會報錯,因為 age ,sex 這兩個參數(shù)強制使用關鍵字參數(shù)
#print_user_info( '兩點水' , 18 , '女' )
print_user_info('兩點水',age='22',sex='男')

通過例子可以看,如果 age , sex 不適用關鍵字參數(shù)是會報錯的。

很多情況下,使用強制關鍵字參數(shù)會比使用位置參數(shù)表意更加清晰,程序也更加具有可讀性。使用強制關鍵字參數(shù)也會比使用 **kw 參數(shù)更好且強制關鍵字參數(shù)在一些更高級場合同樣也很有用。

五、匿名函數(shù)

有沒有想過定義一個很短的回調函數(shù),但又不想用 def 的形式去寫一個那么長的函數(shù),那么有沒有快捷方式呢?答案是有的。

python 使用 lambda 來創(chuàng)建匿名函數(shù),也就是不再使用 def 語句這樣標準的形式定義一個函數(shù)。

匿名函數(shù)主要有以下特點:

  • lambda 只是一個表達式,函數(shù)體比 def 簡單很多。
  • lambda 的主體是一個表達式,而不是一個代碼塊。僅僅能在 lambda 表達式中封裝有限的邏輯進去。
  • lambda 函數(shù)擁有自己的命名空間,且不能訪問自有參數(shù)列表之外或全局命名空間里的參數(shù)。

基本語法

lambda [arg1 [,arg2,.....argn]]:expression

示例:

# -*- coding: UTF-8 -*-

sum = lambda num1 , num2 : num1 + num2;

print( sum( 1 , 2 ) )

輸出的結果:

3

注意:盡管 lambda 表達式允許你定義簡單函數(shù),但是它的使用是有限制的。 你只能指定單個表達式,它的值就是最后的返回值。也就是說不能包含其他的語言特性了, 包括多個語句、條件表達式、迭代以及異常處理等等。

匿名函數(shù)中,有一個特別需要注意的問題,比如,把上面的例子改一下:

# -*- coding: UTF-8 -*-

num2 = 100
sum1 = lambda num1 : num1 + num2 ;

num2 = 10000
sum2 = lambda num1 : num1 + num2 ;

print( sum1( 1 ) )
print( sum2( 1 ) )

你會認為輸出什么呢?第一個輸出是 101,第二個是 10001,結果不是的,輸出的結果是這樣:

10001
10001

這主要在于 lambda 表達式中的 num2 是一個自由變量,在運行時綁定值,而不是定義時就綁定,這跟函數(shù)的默認值參數(shù)定義是不同的。所以建議還是遇到這種情況還是使用第一種解法。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容