1. 程式人生 > >Python學習筆記三:函式

Python學習筆記三:函式

文章目錄


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
  • 有什麼用呢?
    1. 可以用作佔位符,比如現在還沒想好怎麼寫函式的程式碼,就可以先放一個pass,讓程式碼能執行起來;
    2. 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個引數,返回一元二次方程:

a x 2 + b x + c = 0 ax^2 + bx + c = 0

的兩個解。

提示:計算平方根可以呼叫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 n x^n 的函式
# 先寫一個計算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)函式,引數xn都是位置引數
>>> 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
  • 設定預設引數時,有幾個點需要注意:
    1. 必選引數在前,預設引數在後;
    2. 當函式有多個引數時,把變化大引數的放前面,變化小引數的放後面,變化小的可以作為預設引數;
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,…,,計算 a 2 + b 2 + c 2 + . . . . . . . . a^2+b^2+c^2+........
  • 要定義這個函式,我們有兩種方法:
    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
  1. 方法二:可變引數
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()函式為例,檢查是否有cityjob引數:
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}
  • 如果要限制關鍵字引數名字,就會用到命名關鍵字引數,如:只接受cityjob作為關鍵字引數:
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'