標籤:nbsp 特殊 執行 判斷 方法 任務 lin 特殊情況 tar
本節內容
1. 函數基本文法及特性
2. 參數與局部變數
3.遞迴
4.函數式編程介紹
5.高階函數
1.函數基本文法及特性
三種編程範式:
1、面向過程:過程——> def
2、物件導向:類——> class
3、函數式編程:函數——> def
函數是什麼?
函數一詞來源於數學,但編程中的「函數」概念,與數學中的函數是有很大不同的,編程中的函數在英文中也有很多不同的叫法。在C中只有function,在Java裡面叫做method。
定義: 函數是指將一組語句的集合通過一個名字(函數名)封裝起來,要想執行這個函數,只需調用其函數名即可
使用函數的好處:
- 減少重複代碼
- 使程式變的可擴充
- 使程式變得易維護
文法定義
| 12345 |
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‘)
由上得出,過程就是沒有傳回值的函數。
可以帶參數
| 12345678910111213 |
#下面這段代碼a,b = 5,8c = a**bprint(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。
局部變數
| 12345678910 |
name = "Alex Li" def change_name(name): print("before change:",name) name = "金角大王,一個有Tesla的男人" print("after change", name) change_name(name) print("在外面看看name改了麼?",name) |
輸出
| 123 |
before change: Alex Liafter 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. 遞迴
在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞迴函式。
| 12345678910111213 |
def calc(n): print(n) if int(n/2) ==0: return n return calc(int(n/2)) calc(10) 輸出:10521 |
遞迴特性:
1. 必須有一個明確的結束條件
2. 每次進入更深一層遞迴時,問題規模相比上次遞迴都應有所減少
3. 遞迴效率不高,遞迴層次過多會導致棧溢出(在電腦中,函數調用是通過棧(stack)這種資料結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞迴調用的次數過多,會導致棧溢出)
堆棧掃盲http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html
遞迴函式實際應用案例,二分尋找
| 12345678910111213141516171819202122232425 |
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)Function Computex的平方根,只要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.高階函數
變數可以指向函數,函數的參數能接收變數,那麼一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。
即把函數本身當作一個參數,傳給另一個函數。
| 123456 |
def add(x,y,f): #f為一個函數形參 return f(x) + f(y) res = add(3,-6,abs)#abs是函數實參,其實就是函數abs的記憶體位址print(res) |
Python全棧開發-Day3-Python基礎3