1. 程式人生 > >Python(六)語法 函式定義,函式引數

Python(六)語法 函式定義,函式引數

函式

Python內建了很多有用的函式,我們可以直接呼叫。

要呼叫一個函式,需要知道函式的名稱和引數,比如求絕對值的函式abs,只有一個引數。可以直接從Python的官方網站檢視文件:

http://docs.python.org/3/library/functions.html#abs

也可以在互動式命令列通過help(abs)檢視abs函式的幫助資訊。

呼叫abs函式:

>>> abs(100)
100
>>> abs(-20)
20
>>> abs(12.34)
12.34

呼叫函式的時候,如果傳入的引數數量不對,會報TypeError

的錯誤,並且Python會明確地告訴你:abs()有且僅有1個引數,但給出了兩個:

>>> abs(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)

如果傳入的引數數量是對的,但引數型別不能被函式所接受,也會報TypeError的錯誤,並且給出錯誤資訊:str是錯誤的引數型別:

>>> abs('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'

max函式max()可以接收任意多個引數,並返回最大的那個:

>>> max(1, 2)
2
>>> max(2, 3, 1, -5)
3

資料型別轉換

Python內建的常用函式還包括資料型別轉換函式,比如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

函式名其實就是指向一個函式物件的引用,完全可以把函式名賦給一個變數,相當於給這個函式起了一個“別名”:

>>> a = abs # 變數a指向abs函式
>>> a(-1) # 所以也可以通過a呼叫abs函式
1

定義函式

在Python中,定義一個函式要使用def語句,依次寫出函式名、括號、括號中的引數和冒號:,然後,在縮排塊中編寫函式體,函式的返回值用return語句返回。

在Python互動環境中定義函式時,注意Python會出現...的提示。函式定義結束後需要按兩次回車重新回到>>>提示符下

>>> def hello(name):
...  return 'hello:'+name
...
>>> hello('xuxu')
hello:xuxu
>>>

函式體內部的語句在執行時,一旦執行到return時,函式就執行完畢,並將結果返回。因此,函式內部通過條件判斷和迴圈可以實現非常複雜的邏輯。

如果沒有return語句,函式執行完畢後也會返回結果,只是結果為Nonereturn None可以簡寫為return

如果你已經把hello()的函式定義儲存為fun.py檔案了,那麼,可以在該檔案的當前目錄下啟動Python直譯器,用from fun import hello來匯入hello()函式,注意fun是檔名(不含.py副檔名):

>>> from fun import hello
>>> hello('xuxu')
hello:xuxu
>>>

空函式

如果想定義一個什麼事也不做的空函式,可以用pass語句:

def nop():
    pass

pass語句什麼都不做,那有什麼用?實際上pass可以用來作為佔位符,比如現在還沒想好怎麼寫函式的程式碼,就可以先放一個pass,讓程式碼能執行起來。

pass還可以用在其他語句裡,比如:

if age >= 18:
    pass

缺少了pass,程式碼執行就會有語法錯誤。

呼叫函式時,如果引數個數不對,Python直譯器會自動檢查出來,並丟擲TypeError:

>>> hello('x','x')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: hello() takes 1 positional argument but 2 were given

但是如果引數型別不對,Python直譯器就無法幫我們檢查,如下傳入數字型別明顯和之前定義的引數字串型別不一致,但是程式沒有報錯,知道最後返回時,執行到‘hello’+123這一步時才報錯:數值型別無法和字串型別相連

>>> hello(123)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in hello
TypeError: can only concatenate str (not "int") to str

對引數型別做檢查,只允許整數和浮點數型別的引數。資料型別檢查可以用內建函式isinstance()實現

# -*- coding: utf-8 -*-
def hello(name):
 if not isinstance(name,(str)):
  raise TypeError('輸入引數不是字串')
 return('hello:'+name)
>>> hello('1')
'hello:1'
>>> hello(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "E:\python\helloword\fun.py", line 4, in hello
    raise TypeError('輸入引數不是字串')
TypeError: 輸入引數不是字串
>>>

返回多個值

函式可以返回多個值嗎?答案是肯定的。

def hello(x,y):
 return x,y
>>> x,y=hello(1,2)
>>> print(x,y)
1 2
>>>

但其實這只是一種假象,Python函式返回的仍然是單一值

>>> r=hello(1,2)
>>> r
(1, 2)

原來返回值是一個tuple!但是,在語法上,返回一個tuple可以省略括號,而多個變數可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函式返回多值其實就是返回一個tuple,但寫起來更方便。

一道數學題

請定義一個函式quadratic(a, b, c),接收3個引數,返回一元二次方程:

ax2 + bx + c = 0

的兩個解。

提示:計算平方根可以呼叫math.sqrt()函式:

# -*- coding: utf-8 -*-

import math

def quadratic(a,b,c):
 for x in (a,b,c):
  if not isinstance(x,(int,float)):
   raise TypeError('arg type error')
  if a==0:
   return -c/b
  else:
   data=b*b-4*a*c
   if data==0:
    return -b/(2*a)
   elif data<0:
    return '無解'
   elif data>0:
    return (math.sqrt(data)-b)/(2*a),(-math.sqrt(data)-b)/(2*a)
# 測試:
print('quadratic(2, 3, 1) =', quadratic(2, 3, 1))
print('quadratic(1, 3, -4) =', quadratic(1, 3, -4))

if quadratic(2, 3, 1) != (-0.5, -1.0):
    print('測試失敗')
elif quadratic(1, 3, -4) != (1.0, -4.0):
    print('測試失敗')
else:
    print('測試成功')

 

函式的引數

Python的函式定義非常簡單,但靈活度卻非常大。除了正常定義的必選引數外,還可以使用預設引數、可變引數和關鍵字引數,使得函式定義出來的介面,不但能處理複雜的引數,還可以簡化呼叫者的程式碼

位置引數

我們先寫一個計算x2的函式:

>>> def power(x):
...  return x*x
...
>>> power(5)
25

對於power(x)函式,引數x就是一個位置引數。

當我們呼叫power函式時,必須傳入有且僅有的一個引數x

但是如果我們需要計算x的3次方時我們又得寫新的函式,以至於我們寫x的n次方我們得寫無數個函式

此時可以將幾次方N當做函式引數

>>> def power(x,n):
...  return x**n
...
>>> power(2,3)
8

但是這樣一來如果使用者還想像以前一樣直接呼叫power(x)來算平方的時候就會因為缺少必須引數n報錯如下:

>>> power(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: power() missing 1 required positional argument: 'n'

預設引數

這個時候,預設引數就排上用場了。由於我們經常計算x2,所以,完全可以把第二個引數n的預設值設定為2:

>>> def power(x,n=2):
...  return x**n
...
>>> power(2)
4
>>> power(2,3)
8

從上面的例子可以看出,預設引數可以簡化函式的呼叫。設定預設引數時,有幾點要注意:

一是必選引數在前,預設引數在後,否則Python的直譯器會報錯(思考一下為什麼預設引數不能放在必選引數前面);

>>> def add(x,y=2,z):
...  return x+y+z
...
  File "<stdin>", line 1
SyntaxError: non-default argument follows default argument

因為如果使用者呼叫add(1,3) 那麼就會產生歧義,有可能是add(1,2,3),也有可能是add(1,2)

二是如何設定預設引數。

當函式有多個引數時,把變化大的引數放前面,變化小的引數放後面。變化小的引數就可以作為預設引數

使用預設引數有什麼好處?最大的好處是能降低呼叫函式的難度。

多個預設引數時既可以按順序提供預設引數,也可以不按順序提供預設引數,但要帶上引數名

>>> def add(a,b,c=3,d=4):
...  return a+b+c+d
...
>>> add(1,2)
10
>>> add(1,2,4)
11
>>> add(1,2,d=0)
6

add(1,2)相當於引數c和d全部採用預設值

add(1,2,4)相當於引數d採用預設值,也就是1+2+4+4

add(1,2,d=0)相當於指定引數d的值是0,c採用預設值 也就是1+2+3+0

定義預設引數要牢記一點:預設引數必須指向不變物件!舉個例子

>>> def add_end(L=[]):
...  L.append('END')
...  return L
...
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

發現如果將L預設引數指向list那麼每次執行預設方法時都是在後面累加END 而不是一個END

因為L指向的是一個物件,雖然物件一直沒變,但是物件在記憶體中的值改變了,只需要將L指向不變物件即可

>>> def add_end(L=None):
...  if L==None:
...   L=[]
...  L.append('END')
...  return L
...
>>> add_end()
['END']
>>> add_end()
['END']

可變引數

在Python函式中,還可以定義可變引數。顧名思義,可變引數就是傳入的引數個數是可變的,可以是1個、2個到任意個,還可以是0個

在引數前面加了一個*號。在函式內部,引數nums接收到的是一個tuple

>>> def add(*nums):
...  sum=0
...  for num in nums:
...   sum=sum+num
...  return sum
...
>>> add(1,2,3)
6
>>> add()
0
>>>

如果傳入的引數時一個list,Python允許你在list或tuple前面加一個*號,把list或tuple的元素變成可變引數傳進去

>>> list=[1,2,3]
>>> add(*list)
6
>>>

關鍵字引數

可變引數允許你傳入0個或任意個引數,這些可變引數在函式呼叫時自動組裝為一個tuple。而關鍵字引數允許你傳入0個或任意個含引數名的引數,這些關鍵字引數在函式內部自動組裝為一個dict

>>> def person(name,age,**kw):
...  print('name:',name,'age:',age,'other:',kw)
...
>>> person('xuxu',28,city='wuhan',sex='man')
name: xuxu age: 28 other: {'city': 'wuhan', 'sex': 'man'}

傳入dict

>>> dict={'city':'wuhan','sex':'man'}
>>> person('xuxu',28,**dict)
name: xuxu age: 28 other: {'city': 'wuhan', 'sex': 'man'}

命名關鍵字引數

要限制關鍵字引數的名字,就可以用命名關鍵字引數,和關鍵字引數**kw不同,命名關鍵字引數需要一個特殊分隔符**後面的引數被視為命名關鍵字引數,命名關鍵字引數必須傳入引數名,這和位置引數不同。如果沒有傳入引數名,呼叫將報錯

>>> def person(name,*,age):
...  print('name:',name,'age:',age)
...
>>> person('xuxu',age=28)
name: xuxu age: 28
>>> person('xuxu',28)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: person() takes 1 positional argument but 2 were given

如果函式定義中已經有了一個可變引數,後面跟著的命名關鍵字引數就不再需要一個特殊分隔符*

>>> def person(name,*args,age):
...  print('name:',name,args,'age:',age)
...
>>> person('xuxu',age=28)
name: xuxu () age: 28
>>> person('xuxu',1,2,3,age=28)
name: xuxu (1, 2, 3) age: 28
>>>

引數組合

在Python中定義函式,可以用必選引數、預設引數、可變引數、關鍵字引數和命名關鍵字引數,這5種引數都可以組合使用。但是請注意,引數定義的順序必須是:必選引數、預設引數、可變引數、命名關鍵字引數和關鍵字引數。

比如定義一個函式,包含上述若干種引數:

>>> def args(a,b,c=3,*d,e,**kw):
...  print('a:',a,'b:',b,'c:',c,'d:',d,'e:',e,'kw',kw)
...
>>> args(1,2,3,4,4,4,e=5,f=6,g=7)
a: 1 b: 2 c: 3 d: (4, 4, 4) e: 5 kw {'f': 6, 'g': 7}
>>>