前天創了個 Python 微信討論羣,覺得沒人進的,哈哈,想不到還真有小夥伴進羣學習討論。若是想進羣,能夠加我微信: androidwed ,拉進羣,就不貼微信羣二維碼了,一是會失效,二影響文章。python
函數是組織好的,可重複使用的,用來實現單一,或相關聯功能的代碼段。android
自定義函數,基本有如下規則步驟:c++
語法示例:express
def functionname( parameters ):
"函數_文檔字符串"
function_suite
return [expression]複製代碼
實例:編程
def sum(num1,num2):
"兩數之和"
return num1+num2
# 調用函數
print(sum(5,6))複製代碼
輸出結果:微信
11複製代碼
先看一個例子:app
# -*- coding: UTF-8 -*-
def chagne_number( b ):
b = 1000
b = 1
chagne_number(b)
print( b )複製代碼
最後輸出的結果爲:編程語言
1複製代碼
這裏可能有些人會有疑問,爲啥不是經過函數chagne_number
更改了 b
的值嗎?爲啥沒有變化,輸出的結果仍是 1 ,這個問題不少編程語言都會講到,原理解釋也是差很少的。函數
這裏主要是函數參數的傳遞中,傳遞的是類型對象,以前也介紹了 Python 中基本的數據類型等。而這些類型對象能夠分爲可更改類型和不可更改的類型學習
在 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 中的元素值。
這也將影響到函數中參數的傳遞了:
不可更改的類型:相似 c++ 的值傳遞,如 整數、字符串、元組。如fun(a),傳遞的只是 a 的值,沒有影響 a 對象自己。好比在 fun(a)內部修改 a 的值,只是修改另外一個複製的對象,不會影響 a 自己。
可更改的類型:相似 c++ 的引用傳遞,如 列表,字典。如 fun(a),則是將 a 真正的傳過去,修改後 fun 外部的 a 也會受影響
所以,在一開始的例子中,b = 1
,建立了一個整形對象 1 ,變量 b 指向了這個對象,而後經過函數 chagne_number 時,按傳值的方式複製了變量 b ,傳遞的只是 b 的值,並無影響到 b 的自己。具體能夠看下修改後的實例,經過打印的結果更好的理解。
# -*- coding: UTF-8 -*-
def chagne_number( b ):
print('函數中一開始 b 的值:{}' .format( b ) )
b = 1000
print('函數中 b 賦值後的值:{}' .format( b ) )
b = 1
chagne_number( b )
print( '最後輸出 b 的值:{}' .format( b ) )複製代碼
打印的結果:
函數中一開始 b 的值:1
函數中 b 賦值後的值:1000
最後輸出 b 的值:1複製代碼
固然,若是參數中的是可更改的類型,那麼調用了這個函數後,原來的值也會被更改,具體實例以下:
# -*- coding: UTF-8 -*-
def chagne_list( b ):
print('函數中一開始 b 的值:{}' .format( b ) )
b.append(1000)
print('函數中 b 賦值後的值:{}' .format( b ) )
b = [1,2,3,4,5]
chagne_list( b )
print( '最後輸出 b 的值:{}' .format( b ) )複製代碼
輸出的結果:
函數中一開始 b 的值:[1, 2, 3, 4, 5]
函數中 b 賦值後的值:[1, 2, 3, 4, 5, 1000]
最後輸出 b 的值:[1, 2, 3, 4, 5, 1000]複製代碼
經過上面的學習,能夠知道經過 return [表達式] 語句用於退出函數,選擇性地向調用方返回一個表達式。不帶參數值的 return 語句返回 None。
具體示例:
# -*- coding: UTF-8 -*-
def sum(num1,num2):
# 兩數之和
if not (isinstance (num1,(int ,float)) or isinstance (num2,(int ,float))):
raise TypeError('參數類型錯誤')
return num1+num2
print(sum(1,2))複製代碼
返回結果:
3複製代碼
這個示例,還經過內置函數isinstance()
進行數據類型檢查,檢查調用函數時參數是不是整形和浮點型。若是參數類型不對,會報錯,提示 參數類型錯誤
,如圖:
固然,函數也能夠返回多個值,具體實例以下:
# -*- coding: UTF-8 -*-
def division ( num1, num2 ):
# 求商與餘數
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)複製代碼
認真觀察就能夠發現,儘管從第一個輸出值來看,返回了多個值,其實是先建立了一個元組而後返回的。回憶一下,元組是能夠直接用逗號來建立的,觀察例子中的 ruturn ,能夠發現實際上咱們使用的是逗號來生成一個元組。
有時候,咱們自定義的函數中,若是調用的時候沒有設置參數,須要給個默認值,這時候就須要用到默認值參數了。
# -*- 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 函數
print_user_info( '兩點水' , 18 , '女')
print_user_info( '三點水' , 25 )複製代碼
輸出結果:
暱稱:兩點水 年齡:18 性別:女
暱稱:三點水 年齡:25 性別:男複製代碼
能夠看到,當你設置了默認參數的時候,在調用函數的時候,不傳該參數,就會使用默認值。可是這裏須要注意的一點是:只有在形參表末尾的那些參數能夠有默認參數值,也就是說你不能在聲明函數形參的時候,先聲明有默認值的形參然後聲明沒有默認值的形參。這是由於賦給形參的值是根據位置而賦值的。例如,def func(a, b=1) 是有效的,可是 def func(a=1, b) 是 無效 的。
默認值參數就這樣結束了嗎?尚未的,細想一下,若是參數中是一個可修改的容器好比一個 lsit (列表)或者 dict (字典),那麼咱們使用什麼來做爲默認值呢?咱們可使用 None 做爲默認值。就像下面這個例子同樣:
# 若是 b 是一個 list ,可使用 None 做爲默認值
def print_info( a , b = None ):
if b is None :
b=[]
return;複製代碼
認真看下例子,會不會有這樣的疑問呢?在參數中咱們直接 b=[]
不就好了嗎?也就是寫成下面這個樣子:
def print_info( a , b = [] ):
return;複製代碼
對不對呢?運行一下也沒發現錯誤啊,能夠這樣寫嗎?這裏須要特別注意的一點:默認參數的值是不可變的對象,好比None、True、False、數字或字符串,若是你像上面的那樣操做,當默認值在其餘地方被修改後你將會遇到各類麻煩。這些修改會影響到下次調用這個函數時的默認值。
示例以下:
# -*- coding: UTF-8 -*-
def print_info( a , b = [] ):
print(b)
return b ;
result = print_info(1)
result.append('error')
print_info(2)複製代碼
輸出的結果:
[]
['error']複製代碼
認真觀察,你會發現第二次輸出的值根本不是你想要的,所以切忌不能這樣操做。
還有一點,有時候我就是不想要默認值啊,只是想單單判斷默認參數有沒有值傳遞進來,那該怎麼辦?咱們能夠這樣作:
_no_value =object()
def print_info( a , b = _no_value ):
if b is _no_value :
print('b 沒有賦值')
return;複製代碼
這裏的 object
是python中全部類的基類。 你能夠建立 object
類的實例,可是這些實例沒什麼實際用處,由於它並無任何有用的方法, 也沒有任何實例數據(由於它沒有任何的實例字典,你甚至都不能設置任何屬性值)。 你惟一能作的就是測試同一性。也正好利用這個特性,來判斷是否有值輸入。
在 Python 中,能夠經過參數名來給函數傳遞參數,而不用關心參數列表定義時的順序,這被稱之爲關鍵字參數。使用關鍵參數有兩個優點 :
1、因爲咱們沒必要擔憂參數的順序,使用函數變得更加簡單了。
2、假設其餘參數都有默認值,咱們能夠只給咱們想要的那些參數賦值
# -*- 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 函數
print_user_info( name = '兩點水' ,age = 18 , sex = '女')
print_user_info( name = '兩點水' ,sex = '女', age = 18 )複製代碼
輸出的值:
暱稱:兩點水 年齡:18 性別:女
暱稱:兩點水 年齡:18 性別:女複製代碼
有時咱們在設計函數接口的時候,可會須要可變長的參數。也就是說,咱們事先沒法肯定傳入的參數個數。Python 提供了一種元組的方式來接受沒有直接定義的參數。這種方式在參數前邊加星號 *
。若是在函數調用時沒有指定參數,它就是一個空元組。咱們也能夠不向函數傳遞未命名的變量。
例如:
# -*- 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 函數
print_user_info( '兩點水' ,18 , '女', '打籃球','打羽毛球','跑步')複製代碼
輸出的結果:
暱稱:兩點水 年齡:18 性別:女 愛好:('打籃球', '打羽毛球', '跑步')複製代碼
經過輸出的結果能夠知道,*hobby
是可變參數,且 hobby其實就是一個 tuple (元祖)
可變長參數也支持關鍵參數,沒有被定義的關鍵參數會被放到一個字典裏。這種方式便是在參數前邊加 **
,更改上面的示例以下:
# -*- 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 函數
print_user_info( name = '兩點水' , age = 18 , sex = '女', hobby = ('打籃球','打羽毛球','跑步'))複製代碼
輸出的結果:
暱稱:兩點水 年齡:18 性別:女 愛好:{'hobby': ('打籃球', '打羽毛球', '跑步')}複製代碼
經過對比上面的例子和這個例子,能夠知道,*hobby
是可變參數,且 hobby其實就是一個 tuple (元祖),**hobby
是關鍵字參數,且 hobby 就是一個 dict (字典)
關鍵字參數使用起來簡單,不容易參數出錯,那麼有些時候,咱們定義的函數但願某些參數強制使用關鍵字參數傳遞,這時候該怎麼辦呢?
將強制關鍵字參數放到某個*
參數或者單個*
後面就能達到這種效果,好比:
# -*- 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 函數
print_user_info( name = '兩點水' ,age = 18 , sex = '女' )
# 這種寫法會報錯,由於 age ,sex 這兩個參數強制使用關鍵字參數
#print_user_info( '兩點水' , 18 , '女' )
print_user_info('兩點水',age='22',sex='男')複製代碼
經過例子能夠看,若是 age
, sex
不適用關鍵字參數是會報錯的。
不少狀況下,使用強制關鍵字參數會比使用位置參數表意更加清晰,程序也更加具備可讀性。使用強制關鍵字參數也會比使用 **kw
參數更好且強制關鍵字參數在一些更高級場合一樣也頗有用。
有沒有想過定義一個很短的回調函數,但又不想用 def
的形式去寫一個那麼長的函數,那麼有沒有快捷方式呢?答案是有的。
python 使用 lambda 來建立匿名函數,也就是再也不使用 def 語句這樣標準的形式定義一個函數。
匿名函數主要有如下特色:
基本語法
lambda [arg1 [,arg2,.....argn]]:expression複製代碼
示例:
# -*- coding: UTF-8 -*-
sum = lambda num1 , num2 : num1 + num2;
print( sum( 1 , 2 ) )複製代碼
輸出的結果:
3複製代碼
注意:儘管 lambda 表達式容許你定義簡單函數,可是它的使用是有限制的。 你只能指定單個表達式,它的值就是最後的返回值。也就是說不能包含其餘的語言特性了, 包括多個語句、條件表達式、迭代以及異常處理等等。
匿名函數中,有一個特別須要注意的問題,好比,把上面的例子改一下:
# -*- 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 是一個自由變量,在運行時綁定值,而不是定義時就綁定,這跟函數的默認值參數定義是不一樣的。因此建議仍是遇到這種狀況仍是使用第一種解法。