發(fā)布時間: 2018-08-10 14:15:26
5.1 實驗介紹
5.1.1 關于本實驗
本實驗主要介紹了 Python 函數的相關知識點和簡單操作。
5.1.2 實驗目的
1.理解 Python函數的含義。
2.掌握和 Python函數相關的操作。
5.2 實驗任務配置
5.2.1 概念知識
函數可以提高應用的模塊性和代碼的重復利用率。在Python 中,string、tuple和 number 是不可變對象,list、dict是可變對象。對于不可變類型,如整數、字符串、元組,函數調用傳遞的只是值,沒有影響到對象本身。對于可變類型,如列表、字典,函數調用時真的將對象傳過去,修改之后外部的對象也會受到影響。
5.2.2 實驗內容步驟
1 常用內置函數
比如 int 函數可以把其他類型的數據轉化為整數:
>>> int('123')
123
>>> int(12.34)
12
>>> float('12.34')
12.34
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>>bool('')
False
步驟 2 函數名
函數名其實就是指向一個函數對象的引用,完全可以把函數名賦給一個變量,相當于給這個函數起了一個“別名”:
>>>a = abs # 變量 a 指向 abs 函數
>>> a(-1) # 所以也可以通過 a 調用 abs 函數1
步驟 3 定義函數
在 Python 中,定義一個函數要使用 def 語句,依次寫出函數名、括號、括號中的參數和冒號:,然后,在縮進塊中編寫函數體,函數的返回值用 return 語句返回。我們以自定義一個求絕對值的 my_abs函數為例:
>>> def my_abs(x):
ifx>=0:return x
else:return–x
如果想定義一個什么事也不做的空函數,可以用 pass語句, 可以用來作為占位符。修改一下my_abs 的定義,對參數類型做檢查,只允許整數和浮點數類型的參數。數據類型檢查可以用內置函數 isinstance()實現:
def my_abs(x):
ifnot isinstance(x, (int, float)):
raise TypeError('bad operand type')
ifx >= 0:return x
else:return –x
步驟 4 關鍵字參數
可變參數允許你傳入0 個或任意個參數,這些可變參數在函數調用時自動組裝為一個 tuple。而關鍵字參數允許你傳入0 個或任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝為一個 dict。
defperson(name, age, **kw):
print('name:', name, 'age:',age, 'other:', kw)
函數 person 除了必選參數 name 和 age 外,還接受關鍵字參數 kw。在調用該函數時,可以只傳入必選參數:
>>>person('Michael', 30) name:
Michael age:30 other: {}
也可以傳入任意個數的關鍵字參數:
>>>person('Bob', 35, city='Beijing')name:
Bob age: 35 other: {'city': 'Beijing'}
>>>person('Adam', 45, gender='M', job='Engineer')
name:Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
和可變參數類似,也可以先組裝出一個 dict,然后,把該 dict 轉換為關鍵字參數傳進去:
>>>extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack',24, city=extra['city'], job=extra['job'])
name: Jack age: 24 other: {'city':'Beijing', 'job': 'Engineer'}
當然,上面復雜的調用可以用簡化的寫法:
>>>extra = {'city': 'Beijing', 'job': 'Engineer'}
>>>person('Jack', 24, **extra)
name: Jack age: 24 other: {'city':'Beijing', 'job': 'Engineer'}
**extra 表示把 extra 這個 dict 的所有 key-value 用關鍵字參數傳入到函數的**kw 參數,kw 將獲得一個 dict,注意 kw 獲得的 dict 是 extra 的一份拷貝,對kw 的改動不會影響到函數外的extra。
步驟 5 命名關鍵字參數
如果要限制關鍵字參數的名字,就可以用命名關鍵字參數,例如,只接收city 和 job 作為關鍵字參數。這種方式定義的函數如下:
def person(name, age, *, city,job):
print(name, age, city, job)
和關鍵字參數**kw 不同,命名關鍵字參數需要一個特殊分隔符*,*后面的參數被視為命名關鍵字參數。調用方式如下:
>>> person('Jack', 24, city='Beijing',job='Engineer')
Jack24 Beijing Engineer
如果函數定義中已經有了一個可變參數,后面跟著的命名關鍵字參數就不再需要一個特殊分隔符*了:
defperson(name, age, *args, city, job):
print(name, age, args, city, job)
命名關鍵字參數必須傳入參數名,這和位置參數不同。如果沒有傳入參數名,調用將報錯命名關鍵字參數可以有缺省值,從而簡化調用:
def person(name, age, *,city='Beijing', job):
print(name, age, city, job)
由于命名關鍵字參數 city 具有默認值,調用時,可不傳入city 參數:>>>person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer
使用命名關鍵字參數時,要特別注意,如果沒有可變參數,就必須加一個*作為特殊分隔符。如果缺少*,Python 解釋器將無法識別位置參數和命名關鍵字參數。
步驟 6 參數組合
在 Python 中定義函數,可以用必選參數、默認參數、可變參數、關鍵字參數和命名關鍵字參數,這 5 種參數都可以組合使用。但是請注意,參數定義的順序必須是:必選參數、默認參數、可變參數、命名關鍵字參數和關鍵字參數。
比如定義一個函數,包含上述若干種參數:
deff1(a, b, c=0, *args, **kw):
print('a=', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
deff2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c=', c, 'd =', d, 'kw =', kw)
在函數調用的時候,Python 解釋器自動按照參數位置和參數名把對應的參數傳進去。
>>>f1(1, 2)
a= 1 b = 2 c = 0 args = () kw = {
>>>f1(1, 2, c=3)
a =1 b = 2 c = 3 args = () kw = {}
>>>f1(1, 2, 3, 'a', 'b')
a= 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>>f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a','b') kw = {'x': 99}
>>>f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw ={'ext': None}
最神奇的是通過一個 tuple 和 dict,你也可以調用上述函數:
>>>args = (1, 2, 3, 4)
>>>kw = {'d': 99, 'x': '#'}
>>>f1(*args, **kw)
a= 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>>kw = {'d': 88, 'x': '#'}
>>>f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw ={'x': '#'}
所以,對于任意函數,都可以通過類似 func(*args, **kw)的形式調用它,無論它的參數是如何定義的。
步驟 7 遞歸函數
使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由于棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。棧溢出解決方法:
尾遞歸優(yōu)化:解決遞歸調用棧溢出的方法是通過尾遞歸優(yōu)化,事實上尾遞歸和循環(huán)的效果是一樣的,所以, 把循環(huán)看成是一種特殊的尾遞歸函數也是可以的。尾遞歸是指,在函數返回的時候,調用自身本身,并且,return 語句不能包含表達式。這樣,編譯器或者解釋器就可以把尾遞歸做優(yōu)化,使遞歸本身無論調用多少次,都只占用一個棧幀,不會出現棧溢出的情況。上面的 fact(n)函數由于returnn * fact(n - 1)引入了乘法表達式,所以就不是尾遞歸了。要改成尾遞歸方式,需要多一點代碼,主要是要把每一步的乘積傳入到遞歸函數中:
deffact(n):
return fact_iter(n, 1)
deffact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num* product)
可以看到,return fact_iter(num - 1, num* product)僅返回遞歸函數本身,num - 1 和num* product 在函數調用前就會被計算,不影響函數調用。
下一篇: {人工智能}python編程之字符串