函數python
函數容許程序的控制在不一樣的代碼片斷之間切換,函數的重要意義在於能夠在程序中清晰地分離不一樣的任務,將複雜的問題分解爲幾個相對簡單的子問題,並逐個解決。即「分而治之」。程序員
Python的自建模塊通常體現爲函數。Python函數有以下特色:express
(1) 函數是組織好的、可重複使用的,用來實現單一或者相關聯功能的代碼段。編程
(2) 函數首先關注全部任務,而後關注如何完成每項任務。函數類型有兩種:有返回值的函數和僅僅執行代碼而不返回值的函數。數據結構
(3) 函數能提升應用程序的模塊化程度和代碼的重要性。閉包
Python有不少內建函數(即內置函數)例如:print()、int()、float()等。但也能夠本身建立函數,在python中成爲用戶自定義函數。app
函數的定義:模塊化
(1) 語法: def 函數名(參數1,參數2,參數3,,,,):2 函數
「描述信息」3 工具
函數體4
return #用來定義返回值,能夠跟任意數據類型<br><br>
(2)函數定義應該遵循的規則:
return [表達式] 結束函數,選擇性地返回一個值給調用方。不帶表達式的return至關於返回 None。
函數的調用:經過輸入實參來替換形參完成函數的調用
定義時無參,調用時也無參(無參函數)
定義時有參,調用時須要傳參(有參函數)
2.形參與實參
在定義函數時,它的輸入變量被稱爲函數的形參,而執行函數時的輸入變量被稱爲實參。
(1) 參數傳遞-----經過位置和關鍵字
例如:def subtract(x1,x2): #函數定義
return(x1-x2)
位置實參:在調用函數的時候,必須將每一個實參都關聯到函數定義的每個形參,最簡單的關聯方式就是基於實參的順序。
注意:使用位置實參的方式傳值,傳入的實參個數必須與形參相同,不然運行程序會報錯。
經過位置傳遞參數來調用函數,當調用函數subtract時,每一個形參都被實參所取代,只有實參的順序是重要的,實參能夠是任意對象。
z=3
e=subtract(5,z)
關鍵字實參:是經過關鍵字-值的方式,關鍵字實參的方式就不須要考慮函數調用過程當中實參的順序。同一個參數不能傳兩個值
z=3
e=subtract(x2=z,x1=5)
#在這裏的函數調用中,實參時經過名稱賦值給形參而不是經過位置
傳參的規則:
在實參的角度:
規則:按位置傳值必須在按關鍵字傳值的前面
對一個形參只能賦值一次
1.按照位置傳值
2.按照關鍵字傳值
3.混着用
在形參的角度:
規則:默認參數必須放到位置參數的後面
1.位置參數
2.默認參數
3.*args (接收位置傳值)
4.**kwargs(接收關鍵字傳值)
(2) 更改實參
實參的做用是爲函數提供必要的輸入數據,更改函數內部的參數值一般不會影響函數外部的實參值
例如1:對於全部不可變參數(字符串、數字和元組)更改函數內部的實參值一般不會影響函數外部的實參值。
def subtract(x1,x2):
z=x1-x2
x2=50.
return (z)
a=20.
b=subtract(10,a) #返回-10
print(b)
print(a) #返回20.0
示例2:將可變參數(例如:列表或字典)傳遞給函數並在函數內部將其改變,那麼函數外部也會發生改變
def subtract(x):
z=x[0]-x[1]
x[1]=50.
return(z)
a=[10,20]
b=subtract(a)
print(b) #返回-10
print(a) #返回[10,50.0]
(3) 默認參數
默認值是定義函數時已經給出的值。若是在不提供該參數的狀況下調用函數,python將使用程序員在定義函數時所提供的值。
def subtract(x1,x2=0):
z=x1-x2
return (z)
x=subtract(10)
print(x) #必須給出全部的位置參數,只要那些省略的參數在函數定義中有默認值,就沒必要提供全部的關鍵字參數。
注意:可變默認參數:使用可變數據類型的參數做爲默認參數時,若是更改函數內部的可變類型參數,則會產生反作用。例如:
def my_list(x1,x2=[]):
x2.append(x1)
return(x2)
print(my_list(1)) #結果爲:[1]
print(my_list(2)) #結果爲[1,2]
(4) 可變參數:傳入的參數的個數是可變的。
*args 位置參數,表示把args這個list(列表)或者tuple(元組)的全部元素做爲可變參數傳進去
def foo(x,*args): #x爲位置參數, args是可變參數
print(x)
print(args)
foo(1,2,3,4) #1傳給位置參數x,剩下的所有傳給args
foo(1,*(2,3,4)) #能夠直接把一個tupleh或list傳給可變參數args
返回結果爲:1
(2, 3, 4)
**kwargs關鍵字參數:容許傳入0個或者任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝爲一個dict(字典)。
示例:
def foo(x,**kwargs):
print(x)
print(kwargs)
#兩種傳遞方式:**傳遞或者鍵值對傳遞
foo(1,**{"y":2,"z":3}) #能夠直接把一個字典經過加**傳遞給關鍵字參數,
# 返回 1
{'y': 2, 'z': 3}
foo(1,y=2,z=3) #返回結果同上
列表和字典可用來定義或調用參數個數可變的函數。
例如:
import matplotlib.pyplot as plt
data=[[1,2],[3,4]]
style=dict({'linewidth':3,'marker':'o','color':'green'})
plt.plot(*data,**style) #以*爲前綴的變量名稱(*data)是指提供了在函數調用中解包的列表,這樣一來,列表就會產生位置參數。以**爲前綴的變量名稱(**style)是將字典解包爲關鍵字參數。
3. 返回值
return[表達式]用於退出函數。Python中的函數老是返回單個對象。若是一個函數必須返回多個對象,那麼這些對象將被打包並做爲一個元組對象返回。
示例:import numpy as np
def complex_to_polar(z):
r = np.sqrt(z.real**2+z.imag**2)
phi = np.arctan2(z.imag,z.real)
return (r,phi)
z=3+5j
a=complex_to_polar(z)
r=a[0]
phi=a[1] #可寫爲一行:r,phi=complex_to_polar(z)
print(r) 運行結果爲:5.830951894845301
1.0303768265243125
print(phi)
若是函數沒有return語句,則返回None。由於因爲傳遞給函數的變量可能會有所修改,則在不少狀況下,函數不須要返回任何值。
示例:def append_to_list(L,x):
這裏僅提到了列表方法,如append、extend、reverse、sort方法不返回任何值(返回None),當經過這種方法來修改對象時,修改被稱爲原位修改。
4. 遞歸函數
在一個函數內部,能夠調用其餘函數。假如一個函數在其內部能夠調用本身,那麼這個函數是遞歸函數。
遞歸是一種直接和間接地調用函數自身的過程。遞歸的特性有三點:
(1)必須有明確的結束條件。
(2)每次進入更深一層的遞歸時,問題規模相比上次遞歸應有所減小。
(3)遞歸效率不高,遞歸層次過多會致使棧溢出。
遞歸的優勢與缺點:
優勢:遞歸使代碼看起來更加整潔、優雅;能夠用遞歸將複雜任務分解成更加簡單的子問題;
使用遞歸比使用一些嵌套迭代更加容易。
缺點:遞歸的邏輯很難調試、跟進;遞歸調用的代價高昂(效率低)。
例如:def recursion():
return(recursion()) #注意:這個遞歸定義顯然什麼都沒有,若是運行該函數的結果就是一段時間後程序就崩掉了。所以每次調用函數都將會消耗一些內存,當內存爆滿就天然就掛了。這個函數中的遞歸爲無窮遞歸,就比如一個while死循環。
正常的遞歸函數應該包含如下兩個部分:
基線條件(針對最小問題):知足條件時函數將直接返回一個值
遞歸條件:包含一個或者多個調用,這些調用旨在解決問題的一部分。
示例:
#用傳統的循環方式寫:
def factorial(n):
result = n
for i in range(1,n):
result *= i
return result
print(factorial(2))
#經過遞歸的方式實現的,n的階乘看作是n乘以(n-1)的階乘,而1 的階乘爲1
def factorial(n):
if n == 1:
return 1
else:
return n*factorial(n-1)
print(factorial(2))
尾遞歸:
在計算機中,函數調用是經過棧這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀;每當函數返回,棧就會減小一層棧幀。因爲棧的大小不是無限的,因此遞歸調用的次數越多會致使棧溢出。)
爲了防止棧的溢出;咱們能夠使用尾遞歸優化,尾遞歸是指:在函數返回的時候,調用自身自己,而且return語句不能包含表達式。這樣,編譯器或者解釋器就能夠把尾遞歸作優化,使遞歸自己不管調用多少次,都只佔用一個棧幀,不會出現棧溢出的狀況,尾遞歸的實現方式是 :使函數自己返回的是函數自己。
示例以下:
def chebyshev(n,x):
if n==0:
return(1.)
elif n==1:
return(x)
else:
return(2*x*chebyshev(n-1,x)-chebyshev(n-2,x))
a=chebyshev(5,0.52)
print(a) #返回結果:0.39616645119999994
5. 函數文檔、函數是對象、偏函數應用
(1) 文檔字符串:在使用def 關鍵字定義一個函數時, 其後必須跟有函數名和包括形式參數的圓括號。函數體的下一行開始,必須是縮進的。函數體的第一個的邏輯行的字符串,這個字符串就是這個函數的文檔字符串,一般稱做docstring
文檔字符串的定義:
在函數體的第一行,咱們使用一對三個單引號或者一對三個雙引號來定義文檔字符串,文檔字符串一般第一行以大寫字母開頭,以句號結束,第二行是空行,從第三行開始是詳細描述。
文檔字符串的做用:
文檔字符串是咱們使用python過程當中的一個重要的工具,它對文檔頗有幫助,使程序容易理解。甚至當程序運行的時候,咱們能夠從一個函數中返回字符文檔。把函數當作一個對象來看的話,至關於咱們獲取了一個對象的屬性(_doc_)
def printMax(x,y):
'''打印兩個數中的最大值。
兩個數必須都是整形數。'''
x=int(x)
y=int(y)
if x>y:
print(x,'最大')
else:
print(y,'最大')
print(printMax(3,5))
help(printMax) #調用函數help()時,能夠將該文檔字符串餘函數的調用一塊兒進行展現
結果爲:5 最大
None
Help on function printMax in module __main__:
printMax(x, y)
打印兩個數中的最大值。
兩個數必須都是整形數。
補充:查看Python的模塊和函數幫助文檔方法:
Python自帶的查看幫助功能,能夠在編程時不中斷地迅速找到所需模塊和函數的使用方法。
查看模塊下全部函數dir(module_name)
import math
print(dir(math)) #運行結果:列舉出math模塊下全部的函數模塊
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']
查看模塊下特定函數信息help(module_name.func_name)
import math
print(help(math.sin))
運行結果:
Help on built-in function sin in module math:
sin(x, /)
Return the sine of x (measured in radians).
None
查看函數信息的另外一種方法:print(func_name._doc_)
例如:print(print.__doc__)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
(2) 函數是對象,能夠將函數做爲參數傳遞,也可更更名稱或者刪除它們。
(3) 偏函數應用:
(w,t):-f(w,t)=sin(2*np.pi*wt)是一個雙變量函數。對於給定的參數值w,這種解釋解釋將兩個變量中的函數簡化爲變量t。
部分應用程序:這種經過固定(凍結)函數的一個函數或者多個參數來定義新函數的過程稱爲部分應用程序。
偏函數可使用python模塊functools來輕鬆建立,該模塊爲實現這個目的提供一個名爲partial函數。
6. 匿名函數--lambda關鍵字
Python使用lambda來建立匿名函數。所謂匿名,即再也不使用def關鍵字以標準的形式定義一個函數。開發者可能只想對可以用簡單表達式來表示的函數執行操做,而不想對函數進行命名或者經過冗長的def塊來定義函數。
(1) Lambda表達式以下特色:
(2) Lambda函數的語法只包含一個語句,格式以下:
Lambda[arg1,[arg2,......,argn]]:expression
Lambda函數的定義只能由單個表達式組成,尤爲不能包含循環。像其餘函數同樣,lambda函數也能夠做爲對象分配給變量。
注意:使用lambda函數應該注意的幾點:
Lambda定義的單行函數,如須要複雜的函數,應該定義普通函數。
Lambda參數列表能夠包含多個參數,如lambdax,y:x+y
Lambda中的表達式不能含有命令,並且只限一條表達式。
如:parabola=lambda x: x**2+5
print(parabola(3))
示例以下:lambda函數的使用--加法與減法
#自定義函數
sum=lambda arg1,arg2:arg1+arg2
sub=lambda arg1,arg2:arg1-arg2
#調用sum函數
print('相加的值:',sum(10,22))
print('相減的值:',sub(20,5)) 結果爲: 相加的值: 32
相減的值: 15
(4) lambda函數提供了製做閉包的途徑
閉包的含義:一個定義在函數內部的函數,閉包使得變量即便脫離了該函數的做用域範圍也依然能被訪問到(在一個外函數中定義一個內函數,內函數裏運用了外函數的臨時變量,而且外函數的返回值是內函數的引用,這樣就構成了一個閉包)。
7.裝飾器
① 背景:若是想要看看之前寫過的一個函數在數集上的運行時間是多少,這時能夠修改以前的代碼爲它加上新的東西,來實現這樣的功能。但這樣作有些繁瑣,那麼應該採用一種能夠不對源代碼作任何修飾,而且可以很好的實現全部需求的手段--這裏就是用Python裝飾器來實現的。
② 前提是:知道閉包函數,這種函數只能夠在外部函數的做用域內被正常調用,在外部函數的做用域以外調用繪報錯。若是內部函數裏引用了外部函數裏定義的對象(甚至是外層以外,但不是全局變量),那麼此時內部函數就會被稱爲閉包函數,閉包函數所引用的外部定義的變量被叫作自由變量。閉包函數能夠將其本身的代碼和做用域以及外部函數的做用結合在一塊兒。
例如:
def count():
a=1
b=2
def sum():
c=1
return(a+c) #注意:a是自由變量,return sum
③ 定義:裝飾器是python中的語法元素,它能夠不改變函數自己定義的狀況下很方便地變動函數的行爲。Python裝飾器本質上就是一個函數,它可讓其餘函數在不須要代碼變更的前提下增長額外的功能,裝飾器的返回值也是一個函數對象。
裝飾器函數的外部函數傳入我要裝飾的函數名字,返回通過修飾後函數的名字;內層函數(閉包)負責修飾被修飾函數。
④ 裝飾函數屬性:
實質: 是一個函數
參數:是你要裝飾的函數名(並不是函數調用)
返回:是裝飾完的函數名(也非函數調用)
做用:爲已經存在的對象添加額外的功能
特色:不須要對對象作任何的代碼上的變更
⑤ 做用及應用:裝飾函數最大的做用是對於已經寫好的程序,咱們能夠抽離出一些雷同的代碼組建多個特定功能的裝飾器,這樣就能夠針對不一樣的需求去使用特定的裝飾器。
好比應用於插入日誌、性能測試、事務處理、權限校驗等應用場景。
⑥ 示例:爲函數添加計時功能:
示例1. 既不須要侵入,也不須要函數重複執行
import time
def deco(func):
def wrapper():
startTime = time.time()
func()
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapper
@deco #至關於執行func=deco(func),爲func函數裝飾函數並返回
def func():
print("hello")
time.sleep(1)
print("world")
if __name__ == '__main__':
f = func #這裏f被賦值爲func,執行f()就是執行func()
f() #func是要裝飾器的函數,想用裝飾器顯示func函數運行的時間
#分析:裝飾器函數--decorator,該函數傳入參數是被裝飾函數(func),返回參數是內層函數即閉包函數(wrapper),起到裝飾給定函數的做用。其中做爲參數的函數func()就在返回函數wrapper()的內部執行。而後在函數func()前面加上@decorator,func()函數至關於被注入了計時功能,如今只要調用func(),其就已經變爲了「新功能更多」的函數。
#注意:Python中函數返回值爲func和func()的區別:
使用return func返回的func這個函數;
而使用return func()是返回func()執行後的返回值,若是func()函數沒有返回值則返回值是None。
示例2: 帶有不定參數的裝飾器
import time
def deco(func):
def wrapper(*args, **kwargs):
startTime = time.time()
func(*args, **kwargs)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapper
@deco
def func(a,b):
print("hello,here is a func for add :")
time.sleep(1)
print("result is %d" %(a+b))
@deco
def func2(a,b,c):
print("hello,here is a func for add :")
time.sleep(1)
print("result is %d" %(a+b+c))
if __name__ == '__main__':
f = func
func2(3,4,5)
f(3,4) #func()
示例3. 帶有不定參數的多個裝飾器
import time
def deco01(func):
def wrapper(*args, **kwargs):
print("this is deco01")
startTime = time.time()
func(*args, **kwargs)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
print("deco01 end here")
return wrapper
def deco02(func):
def wrapper(*args, **kwargs):
print("this is deco02")
func(*args, **kwargs)
print("deco02 end here")
return wrapper
@deco01
@deco02
def func(a,b):
print("hello,here is a func for add :")
time.sleep(1)
print("result is %d" %(a+b))
if __name__ == '__main__':
f = func
f(3,4)
#func()
運行結果:
this is deco01
this is deco02
hello,here is a func for add :
result is 7
deco02 end here
time is 1001 ms
deco01 end here #注意:多個裝飾器執行的順序就是從最後一個裝飾器開始,執行到第一個裝飾器,再執行函數自己