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