1.函數基本文法及特性
函數是什麼?
函數一詞來源于數學,但程式設計中的「函數」概念,與數學中的函數是有很大不同的,具體差別,我們後面會講,程式設計中的函數在英文中也有很多不同的叫法。在BASIC中叫做subroutine(子過程或子程式),在Pascal中叫做procedure(過程)和function,在C中隻有function,在Java裡面叫做method。
定義: 函數是指将一組語句的集合通過一個名字(函數名)封裝起來,要想執行這個函數,隻需調用其函數名即可
特性:
- 減少重複代碼
- 使程式變的可擴充
- 使程式變得易維護
定義:
1 def Hello():#函數名
2 print('Hello World!')
3 Hello()#調用函數
可以帶參數:
1 def calc(x,y):
2 z = x*y
3 return z
4 c = calc(3,4)
5 print(c)#結果12
2.函數參數與局部變量
形參變量隻有在被調用時才配置設定記憶體單元,在調用結束時,即刻釋放所配置設定的記憶體單元。是以,形參隻在函數内部有效。函數調用結束傳回主調用函數後則不能再使用該形參變量
實參可以是常量、變量、表達式、函數等,無論實參是何種類型的量,在進行函數調用時,它們都必須有确定的值,以便把這些值傳送給形參。是以應預先用指派,輸入等辦法使參數獲得确定值
還是上面的代碼:
1 def calc(x,y): #(x,y)為形參
2 z = x*y
3 return z
4 c = calc(a,b) #(a.b)為實參
5 print(c)
預設參數
1 def stu_register(name, age, country, course):
2 print("----注冊學生資訊------")
3 print("姓名:", name)
4 print("age:", age)
5 print("國籍:", country)
6 print("課程:", course)
7
8
9 stu_register("小麗", 22, "CN", "python_devops")
10 stu_register("小明", 21, "CN", "linux")
11 stu_register("小王", 25, "CN", "linux")
發現 country 這個參數 基本都 是"CN", 就像我們在網站上注冊使用者,像國籍這種資訊,你不填寫,預設就會是 中國, 這就是通過預設參數實作的,把country變成預設參數非常簡單
是以,我們可以通過下面代碼簡化代碼:
def stu_register(name, age, course,country="CN"):
這樣,這個參數在調用時不指定,那預設就是CN,預設你選的值,預設參數!
注意:跟上一段代碼比較,為什麼預設參數放到後面呢?
簡單來講就是會産生歧義。
不妨想一想,如果确實如你所說,有預設值的參數位于無預設值之前,則調用函數時就必須使用key=Value的形式,而不能使用直接送入Value的形式了。
為了調用函數時可以更便捷地使用後者,而同時又不産生歧義,在定義函數時,無預設值參數就必須位于前面了。考慮到定義函數隻要一次,調用函數可能是很多地方、很多人使用,定義函數時稍微注意點是可以了解的了。
關鍵參數
正常情況下,給函數傳參數要按順序,不想按順序就可以用關鍵參數,隻需指定參數名即可,但記住一個要求就是,關鍵參數必須放在位置參數之後。
關鍵字參數:
1 def tes(x=1,y=2):
2 print(x)
3 print(y)
4 tes()
位置參數:
1 def tet1(x,y):
2 print(x)
3 print(y)
4 tet1(1,2)
非固定參數:
若你的函數在定義時不确定使用者想傳入多少個參數,就可以使用非固定參數
#*args參數組,将位置參數(一一對應的位置)放到元組
def tes(*args):
print(args)
tes(1,2,34,5,6)
#結果:(1, 2, 34, 5, 6)
還可以有一個**kwargs
1 def tes(**kwargs):
2 print(kwargs)
3 tes(name='young',age='23',sex='1')
将上述幾個參數組合:
1 def tes(name,age=23,*args,**kwargs):
2 print(name)
3 print(age)
4 print(args)
5 print(kwargs)
6 tes('young',3,5,6,sex='man',job='IT')
7 '''
8 結果:
9 young
10 3
11 (5, 6)
12 {'sex': 'man', 'job': 'IT'}
13 '''
局部變量:
1 name = "哈哈"
2
3 def change_name(name):
4 print("before:", name)
5 name = "Young"
6 print("after", name)
7
8 change_name(name)
9 print("真的改了嗎?", name)
輸出:
before: 哈哈
after Young
真的改了嗎? 哈哈
可以通過global來實作
1 name = "哈哈"
2
3 def change_name(obj):
4 global name
5 print("before:", name)
6 name = "Young"
7 print("after", name)
8
9 change_name(name)
10 print("确實改了!", name)
before: 哈哈
after Young
确實改了! Young
全局與局部變量
在子程式中定義的變量稱為局部變量,在程式的一開始定義的變量稱為全局變量。
全局變量作用域是整個程式,局部變量作用域是定義該變量的子程式。
當全局變量與局部變量同名時:
在定義局部變量的子程式内,局部變量起作用;在其它地方全局變量起作用
3.傳回值
要想擷取函數的執行結果,就可以用return語句把結果傳回
注意:
- 函數在執行過程中隻要遇到return語句,就會停止執行并傳回結果,so 也可以了解為 return 語句代表着函數的結束
- 如果未在函數中指定return,那這個函數的傳回值為None
嵌套函數:
python允許在定義函數的時候,其函數體内又包含另外一個函數的完整定義,這就是我們通常所說的嵌套定義。為什麼?因為函數是用def語句定義的,凡是其他語句可以出現的地方,def語句同樣可以出現。
像這樣定義在其他函數内的函數叫做内部函數,内部函數所在的函數叫做外部函數。當然,我們可以多層嵌套,這樣的話,除了最外層和最内層的函數之外,其它函數既是外部函數又是内部函數。
1 spam = 99
2 def tester():
3 def nested():
4 global spam
5 print('current=',spam)
6 spam += 1
7 return nested
8 #注意:列印 print 那行的代碼調用是tester()()
9 #而不是tester().nested()
4.遞歸
在函數内部,可以調用其他函數。如果一個函數在内部調用自身本身,這個函數就是遞歸函數。
1 def calc(n):
2 print(n)
3 if int(n/2)>0:
4 return calc(int(n/2))
5 print("->",n)
6 calc(10)
1 10
2 5
3 2
4 1
5 -> 1
遞歸特性:
1. 必須有一個明确的結束條件
2. 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減少
3. 遞歸效率不高,遞歸層次過多會導緻棧溢出(在計算機中,函數調用是通過棧(stack)這種資料結構實作的,每當進入一個函數調用,棧就會加一層棧幀,每當函數傳回,棧就會減一層棧幀。由于棧的大小不是無限的,是以,遞歸調用的次數過多,會導緻棧溢出)
堆棧相關知識:http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html
遞歸執行個體, 二分查找:
1 data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
2
3
4 def binary_search(dataset,find_num):
5 print(dataset)
6
7 if len(dataset) >1:
8 mid = int(len(dataset)/2)
9 if dataset[mid] == find_num: #find it
10 print("找到數字",dataset[mid])
11 elif dataset[mid] > find_num :# 找的數在mid左面
12 print("\033[31;1m找的數在mid[%s]左面\033[0m" % dataset[mid])
13 return binary_search(dataset[0:mid], find_num)
14 else:# 找的數在mid右面
15 print("\033[32;1m找的數在mid[%s]右面\033[0m" % dataset[mid])
16 return binary_search(dataset[mid+1:],find_num)
17 else:
18 if dataset[0] == find_num: #find it
19 print("找到數字啦",dataset[0])
20 else:
21 print("沒的分了,要找的數字[%s]不在清單裡" % find_num)
22
23
24 binary_search(data,66)
View Code
5.匿名函數
匿名函數就是不需要顯式的指定函數
1 def calc(n):
2 return n ** n
3 print(calc(3))
4 # 換成匿名函數
5 calc = lambda n: n ** n
6 print(calc(3))
匿名函數主要是和其它函數搭配使用的:
res = map(lambda x:x**2,[1,2,3,4,5])
for i in res:
print(i)
1 4 9 16 25
6.函數式程式設計介紹
函數是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);
這段代碼再演進以下,可以變成這樣
add(1,2).multiply(3).subtract(4)
這基本就是自然語言的表達了。再看下面的代碼,大家應該一眼就能明白它的意思吧:
merge([1,2],[3,4]).sort().search("2")
是以,函數式程式設計的代碼更容易了解。
7.高階函數
變量可以指向函數,函數的參數能接收變量,那麼一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。
def add(a,b,f):
return f(a)+f(b)
res = add(2,-3,abs)
print(res)
8.内置參數
内置參數詳解 https://docs.python.org/3/library/functions.html?highlight=built#ascii
幾個内置方法用法提醒:
#compile
f = open("函數遞歸.py")
data =compile(f.read(),'','exec')
exec(data)
#print
msg = "又回到最初的起點"
f = open("tofile","w")
print(msg,"記憶中你青澀的臉",sep="|",end="",file=f)
# #slice
# a = range(20)
# pattern = slice(3,8,2)
# for i in a[pattern]: #等于a[3:8:2]
# print(i)
#
#
#memoryview
#usage:
#>>> memoryview(b'abcd')
#<memory at 0x104069648>
#在進行切片并指派資料時,不需要重新copy原清單資料,可以直接映射原資料記憶體,
import time
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = data
while b:
b = b[1:]
print('bytes', n, time.time()-start)
for n in (100000, 200000, 300000, 400000):
data = b'x'*n
start = time.time()
b = memoryview(data)
while b:
b = b[1:]
print('memoryview', n, time.time()-start)