Python學習筆記三:函式
阿新 • • 發佈:2018-12-31
文章目錄
1. 呼叫函式
- Python內建了許多有用的函式,可以直接呼叫,例如
abs()
求絕對值函式,可以直接從官方文件檢視函式的名稱和引數,Python函式官方文件介紹;同時也可以在互動式命令列中輸入help(abs)
來檢視abs
函式的介紹; - 在呼叫函式的時候,要注意傳入引數的數量和型別是否正確,否則會報
TypeError
的錯誤;
資料型別轉換
- 內建的函式中還包括資料型別轉換函式,如:
>>> int('123')
123
>>> int(12.123)
12
>>> float('12.123')
12.123
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>> bool('')
False
- 可以把函式名賦值給一個變數,相當於給函式起了一個‘別名’
>>> a = abs
>>> a(-1)
1
2. 定義函式
2.1 def
- 定義一個函式需要用到
def
語句,依次寫出函式名,括號,括號中的引數和冒號:
,然後在縮排塊中編寫函式體,函式的返回量用return
my_abs
函式:
def my_abs(x):
if x >= 0:
return x
else:
return -x
>>> print(my_abs(-99))
99
- 如果把
my_abs()
的函式定義儲存為abstest.py
檔案,那麼,可以在該檔案的當前目錄下啟動Python直譯器,用from abstest import my_abs
來匯入my_abs()
函式,注意abstest
是檔名(不含.py
副檔名):
>>> from abstest import my_abs
>>> my_abs(-9)
9
2.2 空函式
# 定義一個什麼都不做的空函式;
def nop():
pass
- 有什麼用呢?
- 可以用作佔位符,比如現在還沒想好怎麼寫函式的程式碼,就可以先放一個pass,讓程式碼能執行起來;
pass
還可以用在其他語句裡,比如:
if age >= 18:
pass
# 缺少了pass,程式碼執行就會有語法錯誤
2.3 引數檢查
- 當呼叫函式時,如果引數個數不對,會丟擲
TypeError
; - 當引數型別不對時,Python直譯器就無法檢查出來,通過比較自建函式
my_abs
和內建函式abs
的區別,可以看出來;我們可以完善一下這個函式: - 通過內建函式
isinstance()
來做資料型別檢查:
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
2.4 返回多個值
- 在遊戲中經常需要從一個點移到另外一個點,這是需要給出座標,位移和角度,就可以算出新的座標位置:
import math # 匯入math包,允許後續程式碼使用math包裡面的sin,cos函式;
def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y + step * math.sin(angle)
return nx, ny
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0
- 但其實這只是一種假象,Python函式返回的仍然是單一值:
>>> r = move(100, 100, 60, math.pi / 6)
>>> print(r)
(151.96152422706632, 70.0)
- python的函式返回多值其實就是返回一個tuple;
2.5 練習
請定義一個函式quadratic(a, b, c)
,接收3個引數,返回一元二次方程:
的兩個解。
提示:計算平方根可以呼叫math.sqrt()
函式:
import math
def quadratic(a, b, c)
for i in [a, b, c]:
if not isinstance(i, (int, float)):
raise TypeError('bad operand type')
temple = b * b - 4 * a * c
if a == 0:
x0 = -c / b
return x0
elif temple < 0:
return '此方程無解'
elif temple > 0:
x1 = (-b + math.sqrt(temple)) / (2 * a)
x2 = (-b - math.sqrt(temple)) / (2 * a)
return x1, x2
elif temple == 0:
x3 = -b / (2 * a)
return x3
3. 函式的引數
- Python的函式定義非常簡單,靈活度非常大,除了正常定義的必選引數外,還可以使用預設引數、可變引數和關鍵字引數,使得函式定義出來的介面,不但能處理複雜的引數,還可以簡化呼叫者的程式碼;
3.1 位置引數
- 寫一個計算 的函式
# 先寫一個計算x^2的函式;
def power(x):
return x * x # 對於此函式,引數x就是一個位置引數
# 修改為計算x^n的函式;
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = x * s
return s
- 修改後的
power(x, n)
函式,引數x
,n
都是位置引數
>>> power(5, 2)
25
3.2 預設引數
- 此時如果我們再次呼叫舊的函式
power(5)
後,就會報錯,原因是,確少了一個位置引數n
,此時我們可以用預設引數,將第二個引數n
設定為預設值2:
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
>>> power(5)
25
>>> power(5, 2)
25
- 設定預設引數時,有幾個點需要注意:
- 必選引數在前,預設引數在後;
- 當函式有多個引數時,把變化大引數的放前面,變化小引數的放後面,變化小的可以作為預設引數;
def enroll(name, gender, age=6, city='beijing'):
print('name:', name)
print('gender:', gender)
print('age:', age)
print('city:', city)
>>> enroll('a', 'M')
name: A
gender: M
age: 6
city Beijing
>>> enroll('a', 'M', 3, 'shanghai') # 按照順序提供引數
name: a
gender: M
age: 3
city shanghai
>>> enroll('Adam', 'M', city='Tianjin') # 不按照順序提供引數時,需要把引數名寫上
name: Adam
gender: M
age: 6
city Tianjin
- 但預設引數有一個比較坑的地方:
# 先定義一個函式,傳入一個list,新增一個元素後返回
def add_end(L=[]):
L.append('END')
return L
# 當正常呼叫時,一切正常
>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
# 當使用預設引數時,開始出現錯誤了
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
# 預設引數時[],似乎函式每次都記住了上次新增的'END'後的list
- 原因如下:Python函式在定義的時候,預設引數
L
的值就被計算出來了,即[]
,因為預設引數L
也是一個變數,它指向物件[]
,每次呼叫該函式,如果改變了L
的內容,則下次呼叫時,預設引數的內容就變了,不再是函式定義時的[]
了; - 故請注意:預設引數必須指向不變物件!
# 可以通過None這個不變物件來修改上面的例子
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
# 這樣呼叫多少次都不會出現上面的錯誤
3.3 可變引數
- 可變引數是指傳入的引數的個數是可變的;
- 如給定一組數字a, b, c,…,,計算
- 要定義這個函式,我們有兩種方法:
1.方法一:
# 將a, b, c作為一個list或者tuple傳進來,如:
def calc(number):
sum = 0
for i in number:
sum = sum + i * i
return sum
# 但在呼叫的時候,需要先組裝出一個list或tuple
>>> calc([1, 2, 3])
14
>>> calc((1, 2, 3))
14
- 方法二:可變引數
def calc(*number): # 在引數前面加一個*號,在函式內部,引數number接收到的就是一個tuple
sum = 0
for i in number:
sum = sum + i * i
return sum
# 呼叫的時候,可以直接輸入任意個數引數,包括0個引數
>>> calc(1, 2)
5
>>> calc()
0
- 如果已經有一個list或tuple,要呼叫一個可變引數,可用如下方法:
>>> nums = [1, 2, 3]
>>> calc(*nums) # 在list或tuple前加一個*號
14
*nums
表示把nums
這個list的所有元素作為可變引數傳進去;
3.4 關鍵字引數
- 關鍵字引數允許你傳入0個或多個含引數名的引數,這些關鍵字引數在函式內部自動組裝成一個dict,如:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
# 函式除了接受必選引數,還可以接受關鍵字引數kw
>>> person('A', 3)
name: A age: 3 other:{} # 可以只傳入必選引數
>>> person('A', 3, city='beijing') # 可以傳入任意個數的關鍵字引數
name: A age: 3 other: {'city': 'beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
>>> extra = {'city': 'beijing', 'job': 'E'}
>>> person('A', 3, 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
;
3.5 命名關鍵字引數
- 以
person()
函式為例,檢查是否有city
和job
引數:
def person(name, age, **kw):
if 'city' in kw:
pass
if 'job' in kw:
pass
print('name:', name, 'age:', age, 'other:', kw)
- 呼叫者任然可以傳入不受限制的關鍵字引數:
>>> person('A', 3, city='beijing', addr='chaoyang', zipcode=123456)
name: A age: 3 other: {'city': 'beijing', 'addr': 'chaoyang', 'zipcode': 123456}
- 如果要限制關鍵字引數名字,就會用到命名關鍵字引數,如:只接受
city
和job
作為關鍵字引數:
def person(name, age, *, city, job): # *號後面的引數被視為命名關鍵字引數;
print(name, age, city, job)
>>> person('A', 3, city='beijing', job='E')
A 3 beijing E
- 如果函式定義中已經存在一個可變引數
*parameter
,後面跟著的命名關鍵引數就不再需要一個特殊分隔符*
了;
def person(name, age, *args, city, job):
print(name, age, args, city, job)
- 命名關鍵字引數必須傳入引數名,這和位置引數不同,否則會報錯:
>>> person('Jack', 24, 'Beijing', 'Engineer') # 呼叫時缺少引數名city和job,Python直譯器把這4個引數均視為位置引數,但person()函式僅接受2個位置引數;
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given
- 命名關鍵字引數可以有預設值,從而簡化呼叫:
def person(name, age, *, city='beijing', job):
print(name, age, city, job)
>>> person('A', 3, job='E')
A 3 Beijing E
>>> person('A', 3, city='hangzhou', job='E')
A 3 hangzhou E
- 使用命名關鍵字引數時,特別注意,如果沒有可變引數,就必須加一個
*
作為一個特殊分隔符,否則Python直譯器將無法識別位置引數和命名關鍵字引數;
def person(name, age, city, job):
# 缺少 * ,city和job將被識別為位置引數
pass
3.6 引數組合
- 在Python中定義函式時,以上幾種引數都可以同時使用,但請記住,引數定義的順序必須是:必選引數,預設引數,可變引數,命名關鍵字引數和關鍵字引數;
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
>>> 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'