python基礎教程:函式(2)
上一節我們學習了函式的定義和呼叫,理解了基本的函式知識。本節進一步學習函式相關的更多內容,深入瞭解函式,包括:預設引數、關鍵字引數、位置引數、變數的作用域等等。

形參和實參的不同
首先,我們先搞清兩個概念:
形參(parameters),是定義函式時宣告的引數名稱,它定義了函式可以接受的引數型別;
實參(arguments),是呼叫函式時傳給函式的實際值。
比如下面的函式定義:
def func(foo, bar=True, **kwargs): pass
foo, bar, kwargs 是函式的形參(parameters)。
當我們呼叫 func
時,比如:
func(20, bar=False, abc='xyz')
傳給函式的值 20
, False
和 'xyz'
就是實參。
預設引數值
預設引數就是在函式定義時,給引數一個預設值。呼叫該函式時可以不給有預設值的引數傳值,這樣呼叫時的引數可以減少。
看下面的例子:
def say_hi(name, greeting='Hi', more=''): print(greeting, name, more)
這個函式有三個引數,其中 greeting
和 more
有預設值,呼叫該函式時,有以下幾種形式:
- 只傳一個值給name: say_hi(‘Tom’)
- 給可選引數greeting傳值: say_hi(‘Tom’, ‘Hello’)
- 給所有引數傳值: say_hi(‘Tom’, ‘Hello’, ‘how are you’)
一個函式可以有任意多個預設引數,也可以全部引數都有預設值。但是,沒有預設值的引數不能在有預設值的引數的後面,否則會報錯。也就是說,引數的先後順序必須是無預設值引數在前,有預設值引數在後面。
In [175]: def foo(a=1, b): ...:return a + b File "<ipython-input-175-f8e0e97b520a>", line 1 def foo(a=1, b): ^ SyntaxError: non-default argument follows default argument
預設引數值只生成一次,如果預設值是可變物件比如list、dict、set等就會出現詭異的結果,使用時要非常留心。下面的例子,預設值為一個空list,函式呼叫時它把第一個引數放到list裡面。
def func(a, L=[]): L.append(a) return L print(func(1)) print(func(2)) print(func(3))
程式的輸出是:
[1] [1, 2] [1, 2, 3]
這是因為預設值L在函式定義時生成,後面的呼叫(使用預設值,不給L傳值)就會不斷給定義時生成的預設list新增元素。
如果你希望使用這個詭異的特性也沒問題,但要清楚它是什麼。通常我們不建議預設值為可變物件,而是不可變的整數、浮點數、字串等等。
關鍵字引數
呼叫函式時,我們可以使用“關鍵字引數”,它的形式是: kwarg=value
。比如前面的 say_hi
函式的呼叫:
In [180]: say_hi(name='Tom') Hi Tom In [181]: say_hi(name='Tom', greeting='Hello') Hello Tom In [182]: say_hi(name='Tom', more='how are you') Hi Tom how are you In [183]: say_hi(more='good day', name='Tom', greeting='Hi') Hi Tom good day
上面最後一個呼叫告訴我們,關鍵字引數是通過關鍵字來確認引數的,所以可以不用按照函式定義時的順序傳遞引數。
關鍵字引數跟預設引數類似有引數位置的限制,關鍵字引數後面必須都是關鍵字引數。
下面的呼叫都是無效的:
say_hi()# 缺少必須的引數name say_hi(name='Tom', 'Hello')# 關鍵字引數後面出現了非關鍵字引數 say_hi('Tom', name='Tim')# 同樣的引數傳了兩個值 say_hi(age=10)# 函式定義中不存在的關鍵字引數
如果函式定義的最後一個引數是兩個星號加名稱: **name
,那麼它接受一個字典包含所有關鍵字引數,這個字典不包括name前面宣告的普通引數:
In [190]: def foo(a, **kwargs): ...:print(a) ...:for k, v in kwargs.items(): ...:print('%s : %s' % (k, v)) ...: In [191]: foo(1) 1 In [192]: foo(a=1) 1 In [193]: foo(a=1, b=2, c=3, d=4, e='a') 1 b : 2 c : 3 d : 4 e : a
可見, **kwargs
把a後面的所有關鍵字引數都接收了。這對我們以後寫引數非常多的函式時很有幫助。
可變引數列表
可變引數列表類似關鍵字引數 **kwargs
,因為它沒有key只有value,所以它是一個序列(確切的說是一個tuple),它的形式是 *args
,名稱前面有一個星號 *
,用以接收不確定數量的引數。我們常用的內建函式 print
就是一個可變引數函式。
下面我自己定義一個可變引數函式:
In [197]: def foo(*args): ...:print(type(args)) ...:print('|'.join(args)) ...: In [198]: foo('a', 'b', 'c', 'd') <class 'tuple'> a|b|c|d
同樣的,可變引數後面必須跟關鍵字引數:
In [204]: def foo(*args, joiner='|'): ...:print(type(args)) ...:print(joiner.join(args)) ...: In [205]: foo('a', 'b', 'c', 'd') <class 'tuple'> a|b|c|d In [206]: foo('a', 'b', 'c', 'd', joiner='/') <class 'tuple'> a/b/c/d
解包引數列表
這個正好跟可變引數列表相反,如果要呼叫的函式的引數值已經在list或tuple裡面了,我們可以通過解包list或tuple來給函式傳值。比如內建的 range()
函式可以輸入兩個引數:start和stop,如果它們在一個llist或tuple裡面,可以通過 *
操作符解包來傳值:
In [207]: list(range(1, 8)) Out[207]: [1, 2, 3, 4, 5, 6, 7] In [208]: args = [1, 8] In [209]: list(range(*args)) Out[209]: [1, 2, 3, 4, 5, 6, 7]
同樣的,dict可以通過 **
操作符傳遞關鍵字引數:
In [212]: d = {'name':'Tom', 'greeting':'Hello', 'more':'good day'} In [213]: say_hi(**d) Hello Tom good day
lambda函式
通過關鍵字 lambda
來實現小的匿名函式。匿名函式返回一個函式物件,在可以作為函式引數傳遞給函式。比如內建函式 sorted
中的 key
引數就接受一個函式物件。
In [215]: func = lambda a, b: a * b In [216]: func(1,2) Out[216]: 2 In [217]: func(3,5) Out[217]: 15
再看看sorted使用lambda函式的情況,先對學生按姓名排序,再按年齡排序:
In [218]: students = [ ...: {'name': 'Tom', 'age': 12}, ...: {'name': 'Jack', 'age': 13}, ...: {'name': 'Aby', 'age': 10},] In [219]: sorted(students, key=lambda s: s['name']) Out[219]: [{'name': 'Aby', 'age': 10}, {'name': 'Jack', 'age': 13}, {'name': 'Tom', 'age': 12}] In [220]: sorted(students, key=lambda s: s['age']) Out[220]: [{'name': 'Aby', 'age': 10}, {'name': 'Tom', 'age': 12}, {'name': 'Jack', 'age': 13}]
變數的作用域和生命週期
程式中的變數不是在任何地方都可見的,它有自己的作用域。
區域性變數
定義在函式內部的變數只在函式內部可見,也就是說,它是函式的區域性變數。
In [1]: def func(): ...:x = 'hello' ...:print(x) ...: In [2]: func() hello In [3]: x ------------------------ NameErrorTraceback (most recent call last) <ipython-input-3-6fcf9dfbd479> in <module> ----> 1 x NameError: name 'x' is not defined
x是 func
內部的一個變數,對該函式內部可見,所以 print(x)
語句能列印x的值。但是在函式外部訪問x時就會報錯:x是未定義的。
全域性變數
相對於區域性變數,全域性變數是定義在函式外部的,它具有全域性作用域。
In [4]: x = 'hello' In [5]: def func2(): ...:print(x) ...: In [6]: func2() hello In [7]: x Out[7]: 'hello'
如果要在函式內部修改全域性變數,就需要用關鍵字 global
來宣告全域性變數:
In [8]: def func3(): ...:global x ...:x += 'world' ...:print(x) ...: In [9]: func3() helloworld In [10]: x Out[10]: 'helloworld'
區域性變數變數的生命週期從函式呼叫開始,到函式執行結束為止;全域性變數的生命週期直到整個程式結束為止。
刪除函式
前面的章節中,我們使用關鍵字 del
來刪除列表或其中的元素,它同樣可以用來刪除函式:
In [11]: def func4(): ...:print('func4') ...: In [12]: func4() func4 In [13]: del func4 In [14]: func4() ------------------- NameErrorTraceback (most recent call last) <ipython-input-14-0e6ad11a93c1> in <module> ----> 1 func4() NameError: name 'func4' is not defined
在Python中,函式也是物件,所以用 del
刪除函式就跟刪除其它物件一樣。
文件字串(docstring)
作為類、函式或模組之內的第一個表示式出現的字串字面值。它在程式碼執行時會被忽略,但會被直譯器識別並放入所在類、函式或模組的 doc 屬性中。由於它可用於程式碼內省,因此是物件存放文件的規範位置。
In [15]: def my_func(): ...:'''first line is summary of this function ...: ...:the more lines are details of this function ...:''' ...:pass ...: In [16]: print(my_func.__doc__) first line is summary of this function the more lines are details of this function
寫docstring的規則一般是這樣的:
(1)第一行簡短概述該函式或類的功能
(2)第二行空白
(3)後面幾行詳細描述函式的引數、返回值等等
總結
定義函式時,引數稱為“形參”,表述引數的型別;呼叫函式時,引數為“實參”,是傳給函式的具體值。
定義函式時,可以為引數指定預設值;呼叫函式時,可以通過關鍵字引數呼叫。
定義函式是,可以指定引數為可變引數列表 *args
或 **kwargs
;呼叫函式時,可以通過解包list,tuple和dict來傳入引數。
大家在學python的時候肯定會遇到很多難題,以及對於新技術的追求,這裡推薦一下我們的Python學習扣qun:784758214,這裡是python學習者聚集地!!同時,自己是一名高階python開發工程師,從基礎的python指令碼到web開發、爬蟲、django、資料探勘等,零基礎到專案實戰的資料都有整理。送給每一位python的小夥伴!每日分享一些學習的方法和需要注意的小細節
