1、函數(shù)是什么
在Python中,函數(shù)不是數(shù)學(xué)函數(shù)那樣看上去冰冷無聊的規(guī)則和公式,而是有實打?qū)嵉?、有自己作用的代碼。
比如說當我們需要實現(xiàn)“打印”這個功能,會用到print();當我們需要實現(xiàn)“獲取數(shù)據(jù)長度”這個功能,會用到len()。這些都是設(shè)定好了,可以直接拿過來就用的功能,這就叫做“組織好的代碼”。
函數(shù)(Function)能實現(xiàn)的功能從簡單到復(fù)雜,各式各樣,但其本質(zhì)是相通的:“喂”給函數(shù)一些數(shù)據(jù),它就能內(nèi)部消化,給你“吐”出你想要的東西。
這就像自動販賣機,只不過販賣機是喂點錢,吐出來一些吃的喝的用的東西;而Python函數(shù)則是喂各種各樣的數(shù)據(jù),吐出來各種各樣的功能。

上圖中函數(shù)后都帶個小括號,小括號里用于放你輸入的內(nèi)容,這個內(nèi)容就是“參數(shù)”,比如:
print('Hello World')
↑函數(shù)? ? ? ?↑參數(shù)
函數(shù)的定義:函數(shù)是組織好的、可以重復(fù)使用的、用來實現(xiàn)單一功能的代碼。
用販賣機來打比方,販賣機是設(shè)定好可以直接使用(組織好的),可以重復(fù)上架售賣不同的物品(重復(fù)使用),功能是賣東西(單一功能)。
而函數(shù)呢?以print()函數(shù)為例,它也是設(shè)定好可以直接使用(組織好的),不論你想打印什么參數(shù)都可以(重復(fù)使用),而print函數(shù)能實現(xiàn)的單一功能就是“打印”。
那么問題來了,販賣機不一定有我們享要的東西,同理PYTHON自帶的內(nèi)置函數(shù)有限,那我們怎么通過編程,自定獨一無二的函數(shù)呢?
2、定義和調(diào)用函數(shù)
2.1、定義函數(shù)
我們可以通過函數(shù)的語法來定義一個函數(shù)。

比如:
def greet(name):? ? ? ? ? ? ? ? ? #def的意思是定義(define),greet是【函數(shù)名】,name是參數(shù)
? ? print(name+'早上好')? ? ????#函數(shù)(函數(shù)體)要實現(xiàn)的功能:打印出“name+早上好”這句話。
? ? return? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#函數(shù)內(nèi)部一旦遇到return語句,就會停止執(zhí)行并返回結(jié)果。
PS:沒有return語句的函數(shù),Python也會在末尾隱性地加上return None,即返回None值(return None可以簡寫為return)。所以我們經(jīng)常可以省略這個return。
上面這段代碼執(zhí)行后,什么都沒有,一片空白……那是因為我們只定義了函數(shù),還沒有調(diào)用它!
2.1、調(diào)用函數(shù)
函數(shù)咋調(diào)用?喊函數(shù)的名字就可以了!如下
def pika1():
? ? print('我最喜愛的神奇寶貝是皮卡丘')
#該函數(shù)沒有參數(shù),直接調(diào)用函數(shù)名。記得英文括號一定不能少
pika1()????#←←←←←這里就開始喊函數(shù)pika1的名字了
def pika2(name):
? ? print('我最喜愛的神奇寶貝是'+name)
#需要給參數(shù)name賦值,每次調(diào)用都可以給參數(shù)賦不同的值
pika2('皮卡丘')
pika2('噴火龍')
def pika3(name,person):
? ? print('我最喜愛的神奇寶貝是'+name)
? ? print('我最喜愛的馴獸師是'+person)
#需要給兩個參數(shù)分別賦值,并用逗號隔開,否則會報錯
pika3('卡比獸','小智')
函數(shù)的調(diào)用結(jié)果如下:
我最喜愛的神奇寶貝是皮卡丘
我最喜愛的神奇寶貝是皮卡丘
我最喜愛的神奇寶貝是噴火龍
我最喜愛的神奇寶貝是卡比獸
我最喜愛的馴獸師是小智
函數(shù)的調(diào)用關(guān)鍵是:弄清楚這個函數(shù)有多少個參數(shù),如何給參數(shù)賦值,這個過程在函數(shù)里叫做參數(shù)的傳遞(pass)。
函數(shù)能將復(fù)雜的語句和功能統(tǒng)一封裝進一個函數(shù)名里,調(diào)用者只需明白函數(shù)能實現(xiàn)什么,根據(jù)需要傳遞參數(shù)即可。
擴展,tree()函數(shù)打印圣誕樹:
def tree(Height):
? ? print('Merry Christmas!')
? ? for i in range(Height):
? ? ? ? print((Height-i)*2*' '+'o'+ i*'~x~o')
? ? ? ? print(((Height-i)*2-1)*' '+(i*2+1)*'/'+'|'+(i*2+1)*'\\')
tree(6) #通過對tree函數(shù)輸入height參數(shù),即可生成圣誕樹,我們以6層為例
輸出結(jié)果:

3、函數(shù)重要概念
3.1、參數(shù)類型
主要的參數(shù)類型有:位置參數(shù)、默認參數(shù)、不定長參數(shù)。我們用一個案例把這些參數(shù)串起來。
假設(shè)有一家深夜食堂,顧客可以任意點菜。但因為人手不足,所以只能為每個人提供一份開胃菜和一份主食。如果寫成函數(shù)的形式,這個函數(shù)就會有兩個參數(shù)。
(開胃菜和主食的英文分別是appetizer和course)
def menu(appetizer,course):
? ? print('一份開胃菜:'+appetizer)
? ? print('一份主食:'+course+'\n')
#還記得轉(zhuǎn)義字符\n吧,表示換行
menu('牛肉拉面','話梅花生')
menu('話梅花生','牛肉拉面')
#如果采用下面這種形式傳遞,就不需要理會參數(shù)位置。
menu(course='牛肉拉面',appetizer='話梅花生')
運行結(jié)果:

回到這個食堂的故事。經(jīng)營了一陣子之后,為了吸引更多的人流,你決定給每個顧客免費送上一份甜品綠豆沙,這時候你就可以用到【默認參數(shù)】,注意:默認參數(shù)必須放在位置參數(shù)之后。
def menu(appetizer,course,dessert): #在函數(shù)里增加一個甜品就好啦
后來呢,盛夏來襲,你覺得賣燒烤是個不錯的主意。但問題是每個人點的烤串數(shù)量都不同,你也不能限定死數(shù)量,這時候【不定長參數(shù)】就能派上用場,即不確定傳遞參數(shù)的數(shù)量。他的格式是一個星號*加上參數(shù)名。
def menu(*barbeque):
? ? print(barbeque)
menu('烤雞翅','烤茄子','烤玉米')
#這幾個值都會傳遞給參數(shù)barbeque

看,輸出結(jié)果是('烤雞翅', '烤茄子', '烤玉米'),這種數(shù)據(jù)類型就跟元組(tuple)一樣哦。
看,雖然我們只定義了一個參數(shù),但是可以隨便往參數(shù)里加?xùn)|西哦!
現(xiàn)在,讓我們開始點餐吧,用for循環(huán)向不定長參數(shù)里輸入值(燒烤),并把前菜、主菜、甜點一起拼湊為一張菜單吧:
def menu(appetizer,course,*barbeque,dessert='熔巖巧克力'):
? ? print('開胃菜 '+appetizer)
? ? print('主菜 '+course)
? ? print('甜品 '+dessert)
? ? for i in barbeque:? #注意FOR循環(huán)與不定長參數(shù)
? ? ? ? print('燒烤 '+i)? #的用法,i就是這個不定長參數(shù)哦
menu('小紅莓','生烤牛肉','熔巖巧克力++','烤雞翅','烤豬勁肉','烤玉米')

3.2、return的作用
return是返回值,當你輸入?yún)?shù)給函數(shù),函數(shù)就會返回一個值給你。
而print()函數(shù)本身比較特殊,它在屏幕上顯示完相關(guān)的文本內(nèi)容就沒了,并不會返回一個值給我們。所以,它返回的是空值(None)。
在自定義函數(shù)的時候,我們就可以用return語句規(guī)定該函數(shù)要返回什么值給我們。帶return語句的函數(shù)是這樣的:
def niduoda(age):
? ? if age < 12:
? ? ? ? return '哈,是祖國的花朵啊'
? ? elif age < 25:
? ? ? ? return '哇,是小鮮肉呢'
? ? else:
? ? ? ? return '嗯,人生才剛剛開始'
print(niduoda(30))
在上例中,函數(shù)是互相嵌套的,niduoda()函數(shù)就被嵌套在print()函數(shù)里。那為什么在這個例子中,不直接用print,為啥還要用return呢?
因為我們在上面的立體中,要求把函數(shù)功能執(zhí)行后,第一時間把參數(shù)打印出來。而在很多時候,當多個函數(shù)之間相互配合時,我們并不需要第一時間就將結(jié)果打印出來,而是需要將某個返回值先放著,等到需要的時候再做進一步的處理。
return語句-總結(jié)1:如果不是立即要對函數(shù)返回值做操作,那么可以使用return語句保留返回值。
來,讓我們做個案例試試看。
在我們關(guān)于愛情的天真幻想中,我希望我的夢中情人擁有XXX的臉蛋和XXX的身材。
這個需求,需要我們做兩點:一、分別定義兩個函數(shù),參數(shù)為人名,能夠返回字符串'XXX的臉蛋'和'XXX的身材';二、將上述兩個函數(shù)的返回值拼接在一起之后,再打印出來。請你先思考一下,再點擊回車。
def face(name):
? ? return name + '的臉蛋'
def body(name):
? ? return name + '的身材'
print('我的夢中情人:'+face('李若彤') +' + ' + body('林志玲'))
print('我的夢中情人:'+face('新垣結(jié)衣) +' + ' + body(''長澤雅美))? ?#如要再打印,得再輸入調(diào)用
所以更常見的做法是:再定義一個主函數(shù)main(),參數(shù)調(diào)用前兩個函數(shù)的返回值。老師先給出代碼,你可以琢磨一下,主要思考第5行和第6行代碼。
def face(name):
? ? return name + '的臉蛋'
def body(name):
? ? return name + '的身材'
def main(dream_face,dream_body):
? ? return '我的夢中情人:' + face(dream_face) + ' + ' + body(dream_body)
#使用main函數(shù)以后,就只調(diào)用一次了~
print(main('李若彤','林志玲'))
print(main('新垣結(jié)衣','長澤雅美'))
這個代碼有點繞,讓我們做個圖解:

return語句-總結(jié)2:需要多次調(diào)用函數(shù)時,可以再定義一個主函數(shù)main(),調(diào)用非主函數(shù)的返回值。
現(xiàn)在,我們嘗試用另外的方法來一次性返回這兩個值:
def lover(name1,name2):
? ? face = name1 + '的臉蛋'
? ? body = name2 + '的身材'
? ? return face,body
a=lover('李若彤','林志玲')
print('我的夢中情人:'+a[0]+' + '+a[1])
一次性返回的兩個值,是('李若彤的臉蛋', '林志玲的身材')這樣一個元組。
Python語言中的函數(shù)返回值可以是多個,而其他語言都不行,這是Python相比其他語言的簡便和靈活之處。一次接受多個返回值的數(shù)據(jù)類型就是元組。
return語句-總結(jié)3:python的函數(shù)返回值可以是多個,多個返回值的數(shù)據(jù)類型是元組(tuple)。
return語句-總結(jié)4:沒有return語句的函數(shù)會默認返回None值。
示例:
def fun():
? ? a ='I am coding'
print(fun())
運行結(jié)果:None
return語句-總結(jié)5:一旦函數(shù)內(nèi)部遇到return語句,就會停止執(zhí)行并返回結(jié)果。
示例:
def fun():
? return 'I am coding.'
? return 'I am not coding.'
print(fun())
運行結(jié)果:I am coding.
3.3 鞏固練習(xí)
習(xí)題:
一、定義一個帶有兩個參數(shù)的函數(shù),函數(shù)的功能是返回兩個參數(shù)中較大的那個值;二、調(diào)用函數(shù),將99的平方和8888賦值給參數(shù),并將較大值打印出來。
思路:
1、定義存儲兩個參數(shù)的函數(shù),函數(shù)將比較這兩個參數(shù);
2、返回較大的值。
代碼:
def bigdata(num1,num2):
? ? if num1 > num2:
? ? ? ? return num1
? ? else:
? ? ? ? return num2
print(bigdata(99**2,8888))
3.4、變量作用域
當我們定義一個函數(shù)的時候,很重要的事情就是理解函數(shù)中變量的作用域。
第一點:在一個函數(shù)內(nèi)定義的變量僅能在函數(shù)內(nèi)部使用(局部作用域),它們被稱作【局部變量】。
第二點:在所有函數(shù)之外賦值的變量,可以在程序的任何位置使用(全局作用域),它們叫【全局變量】。
例:
x=99 #全局變量x
def num():
? ? x=88 #局部變量x
? ? print(x)
num()
#打印局部變量x
print(x)
#打印全局變量x
因為x=99是在函數(shù)外賦值的,所以第一個變量x是全局變量;x=88是在函數(shù)內(nèi)賦值的,所以第二個變量x是局部變量。

我們可以將定義的函數(shù)想象成一個私人房間,所以里面存數(shù)據(jù)的容器(變量)是私有的,只能在個人的房間里使用;而在函數(shù)外存數(shù)據(jù)的變量是公用的,沒有使用限制。

我們來看看容易新手容易踩的坑:
def egg():?
? ? quantity = 108
egg()
print(quantity)?
運行結(jié)果:NameError: name 'quantity' is not defined,這句話翻譯成中文就是:變量quantity并沒有被定義。其實,我們定義了,只不過是在函數(shù)egg() 內(nèi)定義的,所以這是個局部變量。但問題是我們不能在定義的函數(shù)egg() 外面,也就是不能在全局作用域中使用這個局部變量。
怎么辦呢?其實也很簡單,就像私人房間里的人可以使自由使用公共區(qū)域的物品,在函數(shù)內(nèi)部的局部作用域,是可以訪問全局變量的。
quantity = 108? ? ?#定義變量quantity,這不是在我們定義的函數(shù)內(nèi)的,所以是全局變量。
def egg():
? print(quantity)? ??#函數(shù)內(nèi)的功能是打印變量quantity
egg()? ? ? ? ? ? ? ? ? ?#調(diào)用這個函數(shù)
記住一句話,當變量處于被定義的函數(shù)內(nèi)時,就是局部變量,只能在這個函數(shù)內(nèi)被訪問;當變量處于被定義的函數(shù)外時,就是全局變量,可以在程序中的任何位置被訪問。
如果你非要將局部變量變成全局變量,就像把私人房間的東西挪到公共區(qū)域,可不可以呢?Python也是能夠滿足你的,只不過要用到一種新的語句global語句,就像這樣子:
def egg():
? ? global quantity? ? ? #global語句將變量quantity聲明為全局變量
? ? quantity = 108
egg()
print(quantity)
3.5、練習(xí) 1-Hello Kitty抽獎
練習(xí)目標:
我們會通過今天的項目練習(xí),學(xué)習(xí)函數(shù)的封裝和調(diào)用。?
練習(xí)要求:
我們已經(jīng)有一個hellokitty抽獎器,現(xiàn)在,請你把這個程序封裝成一個新的函數(shù)。
運行抽獎器的代碼:
# 查看注釋,運行代碼。
import random
import time
# 用random函數(shù)在列表中隨機抽獎,列表中只有3位候選者。
luckylist = ['海綿寶寶','派大星','章魚哥']
# random模塊中有個隨機選取一個元素的方法:random.choice()。
a = random.choice(luckylist)? # 從3個人中隨機選取1個人。
print('開獎倒計時',3)
time.sleep(1)? # 調(diào)用time模塊,控制打印內(nèi)容出現(xiàn)的時間
print('開獎倒計時',2)
time.sleep(1)
print('開獎倒計時',1)
time.sleep(1)
# 使用三引號打印hellokitty的頭像
image = '''
/\_)o<
|? ? ? \\
| O . O|
\_____/
'''
print(image)? # ……
print('恭喜'+a+'中獎!')? # 使用print函數(shù)打印幸運者名單
#我了個去,這排版變這樣子,HelloKitty都扭曲了,太難看了,要在程序里跑才能正常。
思路:
這段代碼中,時間調(diào)用模塊是可以用通過for循環(huán)調(diào)用函數(shù)實現(xiàn)的,來試試看吧。
# 查看注釋,運行代碼。
import random
import time
# 用random函數(shù)在列表中隨機抽獎,列表中只有3位候選者。
def main(a,b,c):
? ? luckylist = [a,b,c]
? ? a = random.choice(luckylist) # 從3個人中隨機選取1個人。
# random模塊中有個隨機選取一個元素的方法:random.choice()。
? ? x = 4
? ? for i in range(1,x):
? ? ? ? y = x - i
? ? ? ? print('開獎倒計時',y)
? ? ? ? time.sleep(1) # 調(diào)用time模塊,控制打印內(nèi)容出現(xiàn)的時間
# 使用三引號打印hellokitty的頭像
? ? image = '''
? ? /\_)o<
? ? |? ? ? \\
? ? | O . O|
? ? \_____/
? ? '''
? ? print(image)
? ? print('恭喜'+a+'中獎!')? # 使用print函數(shù)打印幸運者名單
main('張三','李四','王五')

3.6、練習(xí)2-打印52張撲克牌
練習(xí)目標
通過這個練習(xí),你可以通過Python優(yōu)雅地生成一副撲克牌。?
練習(xí)要求
我們將通過這個練習(xí),簡單地復(fù)習(xí)一下return的用法。另外,這個練習(xí)也要求你能夠快速地學(xué)習(xí)新知識并將其運用出來。新知識有3個:一種新的列表生成方式、extend 的新用法和列表生成式。
# 知識1:一種新的列表生成方式
num1 = [1,2,3,4,5]? # 想一想,如果用這個方法生成一個1-100的列表……
num2 = list(range(1,6))
print(num1)
print(num2)
# 知識2:extend 的新用法
num2.extend(['ABCDE'])
num2.extend('ABCDE')? # extend后面是列表的話會將其合并,后面是字符串的話會將每個字符當成一個列表中的元素。
print(num2)
# 知識點3:列表生成式
list1 = [i for i in range(3)]? # 規(guī)定列表中元素的范圍
print(list1)
list2 = [m+n for m in ['天字', '地字'] for n in '一二']? # 列表元素可以是組合,分別規(guī)定范圍。
print(list2)
list3 = [n*n for n in range(1,11) if n % 3 == 0]? # 元素既可規(guī)定范圍,也可附加條件。
print(list3)
通過新知識點,補全函數(shù)cards()。
#每張撲克牌的展現(xiàn)形式是一個元組:(花色,大?。?/p>
# 函數(shù)會返回一個撲克牌列表,里面有52個元組(將花色和數(shù)字分開),對應(yīng)52張牌。(代碼量5行左右)
思路:
1、撲克牌有52張,10以上是JOKA,就是每組13張。則需要預(yù)置元組1存儲花色:黑紅梅方;通過數(shù)字取值范圍+預(yù)置分別定義并組合為元組2存儲牌的大小:2<10<JQKA。
2、循環(huán)遍歷元組1與元組2,將數(shù)據(jù)返還給調(diào)用者,調(diào)用者將返還的數(shù)據(jù)打印出來。
def cards():
? ? # 在下方補充4行代碼(左右),讓函數(shù)下方的打印函數(shù)打印出52張撲克牌。每張撲克牌的展現(xiàn)形式是元組(花色,大小)。
? ? cards_suit =['黑桃','紅桃','梅花','方塊']????????#存儲好花色
? ? cards_range = list(range(2,11))? ? ? ? ? ? ? ? ?#存儲好2~10的牌號
? ? cards_range.extend('JQKA')? ? ? ? ? ? ? ? ? ? ?#把JQKA加入牌號里
? ? return [m+n for m in cards_suit for n in list(map(str,cards_range))]? ??
? ??#把牌號組合好返還,但cards_range中有一部分是數(shù)字,所以組合前要轉(zhuǎn)為str
print(cards())? # 將函數(shù)的返回值打印出來
