python 中的函式定義 原

示例:定義一個斐波拉契數列

>>> def fib(n):    # 打印 Fibonacci 序列到 n
...     """打印到 n 的 Fibonacci 序列."""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b, a+b
...     print()
...
>>> # 現在調用我們剛定義的函式:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

 

幾個概念:

文檔字串(docstring),用於文檔註釋。

符號表(symbol table),函數中的符號表用於該函式的局部變量,因此稱作局部符號表。在函式中被賦值的變量會存儲在局部符號表中。變量引用會先在局部符號表中尋找,然後纔是閉包函式的局部符號表,再然後是全局變量,最後是內建名字表。在函式中的全局變量儘管可以引用,但是不能賦值。函數內賦值只會創建一個新的同名的局部變量。

函式的實參,在它被調用時被引入到這個函式的局部變量表。並且參數是按值傳遞( 總是對象的一個 引用 , 而不是對象本身的值)。當一個函式調用另一個時, 對應這次調用,一個新的局部符號表就會被創建.

函式定義會在當前的符號表裏引入該函式的名字. 函式名對應的值被解釋器認定爲自定義函式類型 函式名的值可以被賦予另一個名字, 使其也能作爲函式使用. 

若函數沒有返回值,默認返回 None(內建名字)。如果要唯一輸出的值是 None, 那麼解釋器會正當的抑制這次返回. 如你實在想看看這個值,可以使用 print():

>>> fib(0)
>>> print(fib(0))
None

* 關於符號表

import dis

c = 3

def abc():
    a = 1
    b = a + c

dis.dis(abc)

輸出

 19           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (a)

 20           6 LOAD_FAST                0 (a)
              9 LOAD_GLOBAL              0 (c)
             12 BINARY_ADD
             13 STORE_FAST               1 (b)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE

 

默認參數

使用默認參數,可以方便進行調用

def meet(you='lisi', him='wangwu'):
    print(you,'meet',him)

meet()
meet('wo')
meet('wo','zhangsan')

重要警告: 默認參數的值只會被求一次值. 但這在默認參數是可變參數的情況下就不一樣了, 如列表, 字典, 或大多類的對象時. 例如, 下面的函式在隨後的調用中會累積參數值:

通常情況

i = 5

def f(arg=i):
    print(arg)

i = 6
f()

# output:
5

特殊情況

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

# output:
[1]
[1, 2]
[1, 2, 3]

如果不希望參數值被後續調用共享:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

* 爲什麼默認參數會變?

默認參數和函數對象本身是一一對應的,一個函數擁有一個默認參數的 tuple。這也很好理解,默認參數隨函數一起定義,並且出現在函數簽名裏,理所應當是函數的一部分。

問題的點在於 Python 中對象傳遞全部爲“引用”,list 對象又都是 mutable 的,所以函數調用時往 list 對象作爲的默認參數中 append 會造成額外副作用。

事實上,Python 開發是倡導用 immutable 對象作爲默認參數的。比如用 None 就是個不錯的選擇:

def foo(bar=None):
    bar = bar or []

又或者用了 mutable 參數一定記得創建副本:

def foo(bar=[]):
    bar = list(bar)

 

關鍵字參數

函數在定義或者調用時,可以指定參數名稱

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.")
    print("-- Lovely plumage, the", type)
    print("-- It's", state, "!")

可以使用下列方法調用(在函式調用時, 關鍵字參數必須跟在位置參數之後. )

parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')

當最後一個形參的形式爲 **name 時, 則排除其他的形參的值, 它將以字典 (參閱 映射類型——字典) 的形式包含所有剩餘關鍵字參數. 這種調用可以與具有 *name 形式的形式參數 (在下一小節中介紹) 聯合使用, 這種形參接受所有超出函式接受範圍的位置參數. ( *name 必須在 **name 之前使用) 例如, 如果我們像這樣定義一個函式:

def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
    print("-" * 40)
    keys = sorted(keywords.keys())
    for kw in keys:
        print(kw, ":", keywords[kw])

它可以如下調用

cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

打印結果(注意, 關鍵字參數名的列表是通過之前對字典 keys() 進行排序操作而創建的; 如果不這樣做, 參數打印的順序是不確定的.)

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

 

任意參數列表

指定函式能夠在調用時接受任意數量的參數. 這些參數會被包裝進一個元組 (參看 元組和序列). 在變長參數之前, 可以使用任意多個正常參數。

def write_multiple_items(file, separator, *args):
    file.write(separator.join(args))

一般地, 這種 variadic 參數必須在形參列表的末尾, 因爲它們將接收傳遞給函式的所有剩餘輸入參數.除此之外,任何出現在 *arg 之後的形式參數只能是關鍵字參數

>>> def concat(*args, sep="/"):
...    return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'

 

參數列表解包

當參數存在於一個既存的列表或者元組之中, 但卻需要解包以若干位置參數的形式被函數調用。

下面是一個利用 * 操作符解從列表或者元組中解包參數以供函數調用的例子

def cute(sex, age, height):
    print(sex,age,height,end=' ')
    print()

t = ['female', 18, 167]
cute(*t)

# output:
female 18 167 

同樣的, 字典可以通過 ** 操作符來解包參數:

d = {'sex': 'male', 'age':18, 'height': 172}
cute(**d)

# output:
male 18 172 

* 或 **操作符有兩個作用:

*arg 表示分散的參數,接受分散的參數。解析後的 arg 表示tuple或者map,可以傳入函數參數爲元組的函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章