Python全棧開發-Day3-Python基礎3
本節內容
1. 函數基本語法及特性
2. 參數與局部變量
3.遞歸
4.函數式編程介紹
5.高階函數
1.函數基本語法及特性
三種編程範式:
1、面向過程:過程——> def
2、面向對象:類——> class
3、函數式編程:函數——> def
函數是什麽?
函數一詞來源於數學,但編程中的「函數」概念,與數學中的函數是有很大不同的,編程中的函數在英文中也有很多不同的叫法。在C中只有function,在Java裏面叫做method。
定義: 函數是指將一組語句的集合通過一個名字(函數名)封裝起來,要想執行這個函數,只需調用其函數名即可
使用函數的好處:
- 減少重復代碼
- 使程序變的可擴展
- 使程序變得易維護
語法定義
1 2 3 4 5 |
def test(x): #函數定義方法、函數名、變量
‘‘‘The function difinition!‘‘‘ #對該函數的描述
x+=1 #函數體
return x #返回值
test(1) #調用函數
|
函數:
def func1():
‘‘‘testing1‘‘‘
print(‘in the func1‘)
return 0
過程:
def func2():
‘‘‘testing2‘‘‘
print(‘in the func2‘)
由上得出,過程就是沒有返回值的函數。
可以帶參數
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#下面這段代碼
a,b = 5 , 8
c = a * * b
print (c)
#改成用函數寫
def calc(x,y):
res = x * * y
return res #返回函數執行結果
c = calc(a,b) #結果賦值給c變量
print (c)
|
return的作用
1、終止函數的運行,即return後面的代碼都將不會被執行
2、return給調用該函數的位置,返回一個值。如,x=test1(),x就會接收到return的返回值
3、後續程序有時需要根據之前函數return返回值的不同,進行不同的判斷和運算。
return的返回值
return 1, ‘hello‘, [‘alex‘,‘wupeiqi‘], {‘name’:‘alex‘}
所以,return的個數沒規定,類型沒規定。把return後面的內容放入元組中全部返回,本質上return還是返回了一個值。如果之前有一個函數def test():, 用return test 時,返回值是test函數的內存地址。
2.函數參數與局部變量
調用方法:
1、test()執行,()表示調用函數test,()可以有參數也可以沒有
參數
1、形參和實參
形參:形式參數,不是實際存在的,是虛擬變量,在定義函數和函數體的時候使用形參,目的是在函數調用時接收實參(實參個數、類型應與形參一一對應)
實參:實際參數,調用函數時傳給函數的參數,可以是常量,變量,表達式,函數,傳給形參
區別:形參是虛擬的,不占用內存空間,形參變量只有在被調用時才分配內存單元,實參是一個變量,占用內存空間,數據傳送單向,實參傳送給形參,不能形參傳給實參。
2、關鍵字調用,與形參順序無關;位置調用與形參一一對應。
【註意】:關鍵字參數不能寫在位置參數之前
3、默認參數
def test(x,y=2):
print(x,y)
test(1)
上述代碼在定義函數時對形參賦值的過程叫做設置默認參數,被賦的值叫做對應形參的默認參數。
默認參數特點:調用函數時,默認參數非必須傳遞
4、參數組:
1)def test(*args):
print(args)
test(1,2,3,4,5) #或者等價於後面這種賦值方式: test(*[1,2,3,4,5])
輸出:(1,2,3,4,5)
這時候會把5個位置實參全部傳給形參args,並且以元組的形式保存下來。定義位置調用參數組時,以*開頭即可,args的名字可以是任意的。但編寫規範就是*args。
2)def test2(**kwargs):
print(kwargs)
test2(name=‘gavin‘,age=25,sex=‘M‘) #或者等價於後面這種賦值方式: test2(**{‘name’:‘gavin‘,‘age‘:25,‘sex‘:‘M‘})
輸出:{‘name’:‘gavin‘,‘age‘:25,‘sex‘:‘M‘}
這時候會把3個關鍵字調用的實參全部傳遞給形參kwargs,並且以字典的方式保存,關鍵字為key,給關鍵字賦的值為value。定義關鍵字調用參數組時,以**開頭,kwargs的名字可以是任意的,但是編寫規範就是**kwargs。
3)小結:
*args把N個位置參數,轉換成元組的方式
**kwargs把N個關鍵字參數,轉換成字典的方式
【註意】: 參數組一定要放在形參的最後面
位置參數一定要寫在關鍵字參數之前。所以對應的,形參部分先寫*args,再寫**kwargs。
局部變量
1 2 3 4 5 6 7 8 9 10 |
name = "Alex Li"
def change_name(name):
print ( "before change:" ,name)
name = "金角大王,一個有Tesla的男人"
print ( "after change" , name)
change_name(name)
print ( "在外面看看name改了麽?" ,name)
|
輸出
1 2 3 |
before change: Alex Li
after change 金角大王,一個有Tesla的男人
在外面看看name改了麽? Alex Li
|
全局與局部變量
在子程序中定義的變量稱為局部變量,在程序的一開始定義的變量稱為全局變量。 全局變量作用域是整個程序(包括所有函數的內部),局部變量作用域是定義該變量的子程序。 當全局變量與局部變量同名時: 在定義局部變量的子程序內,局部變量起作用;在其它地方全局變量起作用。
在函數內修改全局變量
name=‘gavin
def change_name(name):
global name
name=‘Gavin Simons‘
pass
chang_name(name)
在上述這種情況下,在函數內部使用global指令,聲明了name為全局變量,此時在函數內部再修改name時,修改的是全局變量。同時如果在函數內部聲明全局變量之前,即在文件開頭並沒有相同名字的全局變量,則函數內的全局變量會被新創建,在函數外部也可以進行調用。但是,雖然這樣可行,但是永遠不要這麽幹(包括在函數內修改全局變量和在函數內創建全局變量),因為這麽做很難去調試,不知道是那個函數創建或修改了全局變量。
局部變量和全局變量的特殊情況
names=[‘Gavin‘,‘Simons‘,‘Jack‘,‘Rain‘]
def chang_name():
names[0] = ‘詹姆斯西蒙斯’
print(‘inside func‘,names)
change_name()
print(names)
輸出:inside func [‘詹姆斯西蒙斯‘,‘Simons‘,‘Jack‘,‘Rain‘]
[‘詹姆斯西蒙斯‘,‘Simons‘,‘Jack‘,‘Rain‘]
所以只有像簡單的數據類型(比如,字符串和整數)是不能在函數中修改的。但是復雜的數據類型(比如,列表、字典、集合、類,元組在什麽時候都不能改)這些都是在局部裏面可以直接改全局的。因為存放方式和簡單數據類型不同,後者存放的是地址,而不是具體的值。
3. 遞歸
在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def calc(n):
print (n)
if int (n / 2 ) = = 0 :
return n
return calc( int (n / 2 ))
calc( 10 )
輸出:
10
5
2
1
|
遞歸特性:
1. 必須有一個明確的結束條件
2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
3. 遞歸效率不高,遞歸層次過多會導致棧溢出(在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出)
堆棧掃盲http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html
遞歸函數實際應用案例,二分查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
data = [ 1 , 3 , 6 , 7 , 9 , 12 , 14 , 16 , 17 , 18 , 20 , 21 , 22 , 23 , 30 , 32 , 33 , 35 ]
def binary_search(dataset,find_num):
print (dataset)
if len (dataset) > 1 :
mid = int ( len (dataset) / 2 )
if dataset[mid] = = find_num: #find it
print ( "找到數字" ,dataset[mid])
elif dataset[mid] > find_num : # 找的數在mid左面
print ( "\033[31;1m找的數在mid[%s]左面\033[0m" % dataset[mid])
return binary_search(dataset[ 0 :mid], find_num)
else : # 找的數在mid右面
print ( "\033[32;1m找的數在mid[%s]右面\033[0m" % dataset[mid])
return binary_search(dataset[mid + 1 :],find_num)
else :
if dataset[ 0 ] = = find_num: #find it
print ( "找到數字啦" ,dataset[ 0 ])
else :
print ( "沒的分了,要找的數字[%s]不在列表裏" % find_num)
binary_search(data, 66 )
|
4.函數式編程介紹
函數是Python內建支持的一種封裝,我們通過把大段代碼拆成函數,通過一層一層的函數調用,就可以把復雜任務分解成簡單的任務,這種分解可以稱之為面向過程的程序設計。函數就是面向過程的程序設計的基本單元。
函數式編程中的函數這個術語不是指計算機中的函數(實際上是Subroutine),而是指數學中的函數,即自變量的映射。也就是說一個函數的值僅決定於函數參數的值,不依賴其他狀態。比如sqrt(x)函數計算x的平方根,只要x不變,不論什麽時候調用,調用幾次,值都是不變的。
Python對函數式編程提供部分支持。由於Python允許使用變量,因此,Python不是純函數式編程語言。
一、定義
簡單說,"函數式編程"是一種"編程範式"(programming paradigm),也就是如何編寫程序的方法論。
主要思想是把運算過程盡量寫成一系列嵌套的函數調用。舉例來說,現在有這樣一個數學表達式:
(1 + 2) * 3 - 4
傳統的過程式編程,可能這樣寫:
var a = 1 + 2;
var b = a * 3;
var c = b - 4;
函數式編程要求使用函數,我們可以把運算過程定義為不同的函數,然後寫成下面這樣:
var result = subtract(multiply(add(1,2), 3), 4);
因此,函數式編程的代碼更容易理解。
要想學好函數式編程,不要玩py,玩Erlang,Haskell。
5.高階函數
變量可以指向函數,函數的參數能接收變量,那麽一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。
即把函數本身當作一個參數,傳給另一個函數。
1 2 3 4 5 6 |
def add(x,y,f): #f為一個函數形參
return f(x) + f(y)
res = add( 3 , - 6 , abs )#abs是函數實參,其實就是函數abs的內存地址
print (res)
|
Python全棧開發-Day3-Python基礎3