為了提高編寫的效率以及代碼的重用,所以把具有獨立功能的代碼塊組織為一個小模塊,這就是函數(shù)
一、定義函數(shù)
~~~
def 函數(shù)名():
? ? 代碼
Demo(一個打印‘hello word的使用’):
def function():
? ? print("hello word")
~~~
二、調(diào)用
函數(shù)名()
Demo:
?function()
注:我們python是一個非編譯語言,我們在調(diào)用函數(shù)時要在函數(shù)的定義下邊,否則會報錯
在使用過程中調(diào)用函數(shù)名加上一個小括號就可以了,如果不加()那么是一個函數(shù)的引用,不能夠成功調(diào)用
三、函數(shù)的分類
函數(shù)根據(jù)有沒有參數(shù),有沒有返回值,可以相互組合,一共有4種
無參數(shù),無返回值
此類函數(shù),不能接收參數(shù),也沒有返回值,一般情況下,打印提示燈類似的功能,使用這類的函數(shù)
無參數(shù),有返回值
此類函數(shù),不能接收參數(shù),但是可以返回某個數(shù)據(jù),一般情況下,像采集數(shù)據(jù),用此類函數(shù)
有參數(shù),無返回值
此類函數(shù),能接收參數(shù),但不可以返回數(shù)據(jù),一般情況下,對某些變量設(shè)置數(shù)據(jù)而不需結(jié)果時,用此類函數(shù)
有參數(shù),有返回值
此類函數(shù),不僅能接收參數(shù),還可以返回某個數(shù)據(jù),一般情況下,像數(shù)據(jù)處理并需要結(jié)果的應(yīng)用,用此類函數(shù)
四、文檔說明
def demo(a,b):
? ? "用來計算2個數(shù)的和"
? ? return a+b
可以執(zhí)行
help(demo)
查看函數(shù)的相關(guān)說明,一般情況函數(shù)的文檔說明包含:參數(shù)說明、函數(shù)作用、返回值三部分
五、變量
1)局部變量
局部變量,就是在函數(shù)內(nèi)部定義的變量
其作用范圍是這個函數(shù)內(nèi)部,即只能在這個函數(shù)中使用,在函數(shù)的外部是不能使用的
因為其作用范圍只是在自己的函數(shù)內(nèi)部,所以不同的函數(shù)可以定義相同名字的局部變量(打個比方,把你、我是當(dāng)做成函數(shù),把局部變量理解為每個人手里的手機(jī),你可有個iPhone8,我當(dāng)然也可以有個iPhone8了, 互不相關(guān))
局部變量的作用,為了臨時保存數(shù)據(jù)需要在函數(shù)中定義變量來進(jìn)行存儲
當(dāng)函數(shù)調(diào)用時,局部變量被創(chuàng)建,當(dāng)函數(shù)調(diào)用完成后這個變量就不能夠使用了
比如在講到for循環(huán)的時候
for i in range(10):
print(i)
i 就是相當(dāng)于局部變量 在其他位置不能使用只能在for循環(huán)中使用。
在函數(shù)中局部變量就是在函數(shù)中定義的變量
?2)全局變量
如果一個變量,既能在一個函數(shù)中使用,也能在其他的函數(shù)中使用,這樣的變量就是全局變量
打個比方:有2個兄弟 各自都有手機(jī),各自有自己的小秘密在手機(jī)里,不讓另外一方使用(可以理解為局部變量);但是家里的電話是2個兄弟都可以隨便使用的(可以理解為全局變量)
全局變量可以說在函數(shù)外面定義的變量,所有函數(shù)都可以使用到
demo:
# 定義全局變量
a =100
deftest1():
????print(a)# 雖然沒有定義變量a但是依然可以獲取其數(shù)據(jù)
deftest2():
????print(a)# 雖然沒有定義變量a但是依然可以獲取其數(shù)據(jù)
# 調(diào)用函數(shù)
test1()
test2()
全局變量能夠在所有的函數(shù)中進(jìn)行訪問
全局變量和局部變量名字相同
demo:
a = 100 #定義全局變量
def func():
????a = 10 # 定義局部變量,和全局變量名相同
? ? print(a) #函數(shù)內(nèi)在輸出時是局部的10,而不是100
print(a) #函數(shù)外面輸出100 局部變量在函數(shù)外面無法訪問
局部變量會覆蓋全局變量,在局部變量所在的塊或者函數(shù)內(nèi),對變量的操作不影響全局變量的值,全局變量不起作用,在局部變量所在的代碼塊或函數(shù)外部,全局變量才起作用。
這就是變量作用域的問題。
如果我們想在函數(shù)中修改全局變量而不是局部變量,那么我們需要使用global進(jìn)行聲明
a = 100 #定義全局變量
def func():
????global a # 聲明使用全局變量
????a = 10
? ? print(a) #在輸出時是局部的10
print(a) # 輸出10?global 聲明后將全部變量進(jìn)行修改
六、參數(shù)
為了讓一個函數(shù)更通用,即想讓它計算哪兩個數(shù)的和,就讓它計算哪兩個數(shù)的和,在定義函數(shù)的時候可以讓函數(shù)接收數(shù)據(jù),就解決了這個問題,這就是 函數(shù)的參數(shù)(接收到的參數(shù)就可以當(dāng)做局部變量來使用,python中沒有指針,參數(shù)的傳遞有兩種方式:值傳遞、引用傳遞。一般我們用的是引用傳遞)
參數(shù)在這里有分為形參和實參。我們可以理解形參就是函數(shù)上面定義時接收的變量名,實參就是調(diào)用的時候傳遞的參數(shù)。一般我們講到的參數(shù)就是指的形參,即函數(shù)定義時的參數(shù)。
1)形參
demo:
#定義一個函數(shù)作用是進(jìn)行兩個數(shù)進(jìn)行相加,在這里a,b就是參數(shù)
def add2num(a,b):
? ? c = a +b
? ? pring(c)
add2num(10,20) #調(diào)用函數(shù)計算兩個數(shù)的和
我們的python解析器首先去定義函數(shù)add2num,然后在進(jìn)行時調(diào)用函數(shù),調(diào)用時傳遞兩個參數(shù),分別給a,b
然后程序會走到c = a + b進(jìn)行計算,最后打印結(jié)果。
*? 缺省參數(shù)(在形參中默認(rèn)有值的參數(shù),稱之為缺省參數(shù))
調(diào)用函數(shù)時,缺省參數(shù)的值如果沒有傳入,則取默認(rèn)值。
demo:
下例會打印默認(rèn)的age,如果age沒有被傳入:
def printinfo(name, age=35):
????# 打印任何傳入的字符串
????print("name: %s"% name)??
?????print("age %d"% age)
# 調(diào)用printinfo函數(shù)
printinfo(name="miki")
# 在函數(shù)執(zhí)行過程中 age去默認(rèn)值35
printinfo(age=9,name="miki")
以上實例輸出結(jié)果:
name: miki
age:35
name: miki
age:9
注意:帶有默認(rèn)值的參數(shù)一定要位于參數(shù)列表的最后面
?* 不定長參數(shù)(有時可能需要一個函數(shù)能處理比當(dāng)初聲明時更多的參數(shù), 這些參數(shù)叫做不定長參數(shù),聲明時不會命名。)
def functionname([formal_args,] *args, **kwargs):
????"""函數(shù)_文檔字符串"""
????function_suite
????return[expression]
加了星號(*)的變量args會存放所有未命名的變量參數(shù),args為元組
而加**的變量kwargs會存放命名參數(shù),即形如key=value的參數(shù), kwargs為字典.
deffun(a, b, *args, **kwargs):
?"""可變參數(shù)演示示例"""
? ? ?print("a =%d"% a)
? ? ?print("b =%d"% b)
? ? ?print("args:")
? ? ?print(args)
? ? ?print("kwargs: ")
? ? ?forkey, valueinkwargs.items():
? ? ?????print("key=%s"% value)
?fun(1,2,3,4,5, m=6, n=7, p=8)# 注意傳遞的參數(shù)對應(yīng)
以上打印結(jié)果:
a =1
b =2
args:(3,4,5)
kwargs:?
p =8
m =6
n =7
第二次傳遞參數(shù):
?c = (3,4,5)
?d = {"m":6,"n":7,"p":8}
?fun(1,2, *c, **d)# 注意元組與字典的傳參方式
打印結(jié)果
a =1
b =2
args:(3,4,5)
kwargs:
?p =8
m =6
n =7
第三次調(diào)用
fun(1,2, c, d)# 注意不加星號與上面的區(qū)別
打印結(jié)果:
a =1
b =2
args:
((3,4,5), {'p':8,'m':6,'n':7})
kwargs:
注:如果很多個值都是不定長參數(shù),那么這種情況下,可以將缺省參數(shù)放到 *args的后面, 但如果有**kwargs的話,**kwargs必須是最后的
2)實參
?實參就是函數(shù)在調(diào)用時小括號里面寫入的參數(shù),實參可以傳遞一個值,也可以是一個引用。在后面的學(xué)習(xí)里形參還可以傳遞一個對象,或者是一個函數(shù)。因為他們都是一個引用,可以當(dāng)做一個參數(shù)來傳遞。
注意在使用中傳遞的參數(shù)實參數(shù)據(jù)類型要和接收方形參的數(shù)據(jù)類型一致。
七、返回值
所謂“返回值”,就是程序中函數(shù)完成一件事情后,最后給調(diào)用者的結(jié)果
想要在函數(shù)中把結(jié)果返回給調(diào)用者,需要在函數(shù)中使用return
如下示例:
def add2num(a, b):
????c = a+b
????return c
在返回后的數(shù)據(jù)一般情況我的會使用或者保存的這個值,那么我們可以用一個變量名去接收
#調(diào)用函數(shù),順便保存函數(shù)的返回值
result = add2num(100,98)
print(result)
1)返回多個值
有的函數(shù)返回的結(jié)果不僅僅只有一個,那么我們應(yīng)該怎么做呢?
defdivid(a, b):
? ??shang = a//b
? ???yushu = a%b
? ??return shang, yushu#默認(rèn)是元組
result = divid(5,2)
print(result)# 輸出(2, 1)
返回的值是一個元組,我們可以進(jìn)行拆包
a,b =??divid(5,2)
print(a) #打印結(jié)果為2
print(b) #打印結(jié)果為1
注:返回的數(shù)值一定要一一對應(yīng)才可以正常拆包
return后面可以是元組,列表、字典等,只要是能夠存儲多個數(shù)據(jù)的類型,就可以一次性返回多個數(shù)據(jù)
deffunction():
? ??# return [1, 2, 3]
? ??# return (1, 2, 3)
? ??return{"num1":1,"num2":2,"num3":3}
2)多個return
defcreate_nums():
? ??print("---1---")
? ??return1# 函數(shù)中下面的代碼不會被執(zhí)行,因為return除了能夠?qū)?shù)據(jù)返回之外,還有一個隱藏的功能:結(jié)束函數(shù)
? ??print("---2---")
? ??return2
? ??print("---3---")
一個函數(shù)中可以有多個return語句,但是只要有一個return語句被執(zhí)行到,那么這個函數(shù)就會結(jié)束了,因此后面的return沒有什么用處。
def create_nums(num):
? ??print("---1---")
? ??if num ==100:?
?? ??????print("---2---")
? ??????return?num+1# 函數(shù)中下面的代碼不會被執(zhí)行,因為return除了能夠?qū)?shù)據(jù)返回之外,還有一個隱藏的功能:結(jié)束函數(shù)
? ??else:?
?? ??????print("---3---")
? ??????return num+2
?result1 = create_nums(100)?
?print(result1)# 打印101
result2 = create_nums(200)
?print(result2)# 打印202
八、匿名函數(shù)
用lambda關(guān)鍵詞能創(chuàng)建小型匿名函數(shù)。這種函數(shù)得名于省略了用def聲明函數(shù)的標(biāo)準(zhǔn)步驟。
lambda函數(shù)的語法只包含一個語句,如下:
lambda[arg1 [,arg2,.....argn]]:expression
如下實例:
sum =lambda arg1, arg2: arg1 + arg2
#調(diào)用sum函數(shù)
print("Value of total : ", sum(10,20))
print("Value of total : ", sum(20,20))
以上實例輸出結(jié)果:
Value of total :30
Value of total :40
Lambda函數(shù)能接收任何數(shù)量的參數(shù)但只能返回一個表達(dá)式的值
匿名函數(shù)不能直接調(diào)用print,因為lambda需要一個表達(dá)式
lambda函數(shù)一般情況下會當(dāng)做一個參數(shù)來傳遞,比如做字典的排序?;蛘叻诺礁呒壓瘮?shù)中使用,后面會有講到。
九、函數(shù)的嵌套調(diào)用
在一個函數(shù)中嵌套調(diào)用另外一個函數(shù),就是函數(shù)的嵌套調(diào)用。
deftestB():
? ??print('---- testB start----')?
? ???print('這里是testB函數(shù)執(zhí)行的代碼...(省略)...')?
? ???print('---- testB end----')
deftestA():
? ??print('---- testA start----')?
? ???testB()?
? ???print('---- testA end----')?
testA()
運行結(jié)果:
---- testA start----
? ? ---- testB start----
? ? 這里是testB函數(shù)執(zhí)行的代碼...(省略)...
? ? ---- testB end----
? ? ---- testA end----

十、遞歸
如果一個函數(shù)在內(nèi)部不調(diào)用其它的函數(shù),而是自己本身的話,這個函數(shù)就是遞歸函數(shù)。
舉個例子,我們來計算階乘?n! = 1 * 2 * 3 * ... * n
def func(num):
? ? if num > 1:
? ? ? ? reault = num *?func(num-1)
? ? else:
? ? ? ? reault = 1
? ? return?reault
func(5)

十一、閉包
閉包也具有提高代碼可復(fù)用性的作用。后邊講到的裝飾器就是用到閉包的原理
# 定義一個函數(shù)
def test(number):
# 在函數(shù)內(nèi)部再定義一個函數(shù),并且這個函數(shù)用到了外邊函數(shù)的變量,那么將這個函數(shù)以及用到的一些變量稱之為閉包
? ??def test_in(number_in):
? ??????print("in test_in 函數(shù), number_in is %d"% number_in)
? ??????return number+number_in #number_in其實是一個函數(shù)的引用,是把函數(shù)的引用當(dāng)做參數(shù)來傳遞
? ??# 其實這里返回的就是閉包的結(jié)果
? ??return test_in? #返回一定是內(nèi)部函數(shù)的引用,不能帶有小括號
# 給test函數(shù)賦值,這個20就是給參數(shù)
ret = test(20)? ? ? ? # 在這里執(zhí)行的是test函數(shù),然后將test_in函數(shù)的引用返回,重新賦值給ret
# 注意這里的100其實給參數(shù)
print(ret(100))? ?#在這里使用的ret(100)? 相當(dāng)于test_in(100)? 也就相當(dāng)于 test(20)(100) 最終返回的是 20+100
#注 意這里的200其實給參數(shù)
print(ret(200))???#在這里使用的ret(100)? 相當(dāng)于test_in(200)? 也就相當(dāng)于 test(20)(200) 最終返回的是 20+200
由于閉包引用了外部函數(shù)的局部變量,則外部函數(shù)的局部變量沒有及時釋放,消耗內(nèi)存