天天看點

簡明Python3教程 9.函數

簡介

函數是程式的可複用片段,允許你為語句塊賦予名字之後在程式的任何地方運作它們任意次,這稱做函數調用。

我們已經使用過一些内建函數,例如len和range等。

函數也許是任何有意義的軟體中最重要的構件,是以我們将在本章探究函數的方方面面。

函數以關鍵字def定義,其後緊跟函數名,由一對小括号閉合的形參,最後以冒号結束定義行,

定義行下面的是函數體,它是一個語句塊。

聽着有點複雜,其實定義起來是很簡單的,見下面的例子:

範例:

#!/usr/bin/python

# Filename: function1.py

def sayHello():

    print('Hello World!') # 語句塊也就是函數體

# 結束函數定義

sayHello() # 調用函數

sayHello() # 再次調用

輸出:

    $ python function1.py

    Hello World!

工作流程:

我用使用上面講解的函數定義文法定義了一個名叫sayHello的函數,這個函數沒有形參是以小括号裡也就沒有

定義變量。函數形參隻是提供給函數的輸入,是以我們可以為函數傳遞不同的形參而後得到相應的結果。

注意我們調用了相同的函數兩次,這也意味着我們無需多次編寫相同的代碼(注:函數體)。

函數形參

一個函數可以擁有形參,它們是你提供給函數的值,是以函數就可以利用這些值進行一些操作了。

函數形參非常類似變量,隻是這些變量是在我們調用函數時定義的并在函數運作前被指派。

形參在函數定義中的小括号内指定,并以逗号分隔。當我們調用函數時以同樣的方式提供形參值。

注意下面兩個術語的差別 – 函數定義中給定的參數叫做形參,而在函數調用提供的值叫做 – 實參

# Filename: func_param.py

def printMax(a, b):

    if a > b:

        print(a, 'is maximum')

    elif a == b:

        print(a, 'is equal to', b)

    else:

        print(b, 'is maximum')

printMax(3, 4) # 直接提供字面值

x = 5

y = 7

printMax(x, y) # 以變量作為實參

輸出:

    $ python func_param.py

    4 is maximum

    7 is maximum

我們定義的函數叫做printMax,它需要兩個形參,分别叫做a和b。

函數利用簡單的if…else…語句找出兩者中較大的數并将其列印。

第一次調用printMax時我們直接使用數字作為實參,而第二次調用時我們使用變量。

printMax(x, y)促使實參x的值賦給形參a, y賦給b。

兩次調用中,pringMax的工作方式完全相同。

局部變量

在函數内聲明的變量與在函數外的同名變量沒有任何關系,即變量名對于函數是局部的。

這被稱作變量的作用域,變量的作用域開始于它們所在塊中定義它們的定義點處。

# Filename: func_local.py

x = 50

def func(x):

    print('x is', x)

    x = 2

    print('Changed local x to', x)

func(x)

print('x is still', x)

    $ python func_local.py

    x is 50

    Changed local x to 2

    x is still 50

如何工作:

在函數内部,我們首先使用變量x的值時,python引用的是函數形參x的值。

接下來,我們為x指派2,因為x是這個函數的局部名字,是以當我們在函數中使用x的時候,

不會影響到主塊中的x ,在最後的print調用中我們列印了主塊中x的值也證明了這點。

使用global語句

當你希望為程式的頂級名字指派時(沒有定義在任何其他作用域中的變量,比如函數或類作用域),

(注: python有名字空間的概念,名字空間建立起名字與實際對象(/資料)的映射關系,

這裡的”名字”指的是名字所對應的對象)。這時你必須告訴python,名字不是局部而是全局的。

通過global語句可以做到這點,否則是不可能對一個定義在函數外的變量指派的。

你可以使用定義在函數外的變量的值(但要假設函數内沒有同名變量),不過并不鼓勵這種用法而是應該

盡量避免,這會讓代碼的讀者難以弄清變量到底定義在哪?

# Filename: func_global.py

def func():

    global x

    print('Changed global x to', x)

func()

print('Value of x is', x)

    $ python func_global.py

    Changed global x to 2

    Value of x is 2

global語句用于聲明x是一個全局變量 – 是以我們在函數内為x指派後變化也會反映在主塊中對x的使用中。

你也可以使用一個global語句指定多個全局變量,比如global x, y, z。

使用nonlocal語句

我們已經看到如何存取局部和全局變量。還有另一類叫做”非局部”的作用域,它介于局部和全局之間。

當在函數内定義函數時你會注意到非局部作用域。

因為python中的一切隻是可執行代碼,是以你可以在任何地方定義函數。讓我們看一個例子:

# Filename: func_nonlocal.py

def func_outer():

    def func_inner():

        nonlocal x

        x = 5

    func_inner()

func_outer()

    $ python func_nonlocal.py

    x is 2

    Changed local x to 5

當我們處于func_inner函數中時,func_outer第一行定義的變量x既不是局部也不是全局變量。

這時通過nonlocal x我們聲明這個x是非局部的,是以我們才能夠存取它。

試着将nonlocal x改為global x觀察這兩種用法有什麼不同。

預設實參值

對于一些函數,你可能希望它們的形參是可選的,并當使用者沒有為這些形參提供值的時候給它們一個預設值。

這需要借助預設實參值。預設實參值在函數定義時通過為形參名賦一個預設值實作。

注意預設實參值應該是一個常量,更确切的應該是一個不可變類型 – 後面的章節會有具體解釋,現在隻要記住這

點就可以了。

# Filename: func_default.py

def say(message, times = 1):

    print(message * times)

say('Hello')

say('World', 5)

    $ python func_default.py

    Hello

    WorldWorldWorldWorldWorld

函數say用于以指定次數列印指定字元串. 如果我們沒有指定次數則預設的字元串隻被列印1次。

我們通過為形參times指定一個預設實參值1實作這個功能。

第一次調用say時,我們隻提供了被列印的字元串然後函數隻列印一次。

而第二次調用時,我們不僅提供了被列印的字元串還提供了實參5以表明我們需要字元串被列印5次。

重點:

隻有在實參清單靠後的參數才能擁有預設實參值,即你不能先聲明帶有預設實參值的形參再聲明不帶有預設實參值的參數。

這是因為實參值是根據形參的位置賦給形參的,例如:def func(a, b = 5)合法,但def func(a = 5, b)就非法了。

關鍵實參

如果你有一些函數擁有許多參數,但你隻想使用其中的幾個,這時你可以通過形參名為其指派。

這被稱做關鍵實參- 使用形參名(關鍵字)為函數指定實參而不是我們一直使用的通過位置指定實參。

這樣做有兩個優點,首先函數用起來更簡單,因為我們不用操心實參的順序了。

其次,可以隻為我們感興趣的形參指派,如果其它參數帶有預設實參值的話。

# Filename: func_key.py

def func(a, b=5, c=10):

    print('a is', a, 'and b is', b, 'and c is', c)

func(3, 7)

func(25, c=24)

func(c=50, a=100)

    $ python func_key.py

    a is 3 and b is 7 and c is 10

    a is 25 and b is 5 and c is 24

    a is 100 and b is 5 and c is 50

函數func擁有一個不帶有預設實參值的參數,後跟兩個帶有預設實參值的參數。

第一次調用func(3, 7),形參a得到值3,b為7,c為預設值10。

第二次調用func(25, c=24), 因為a在參數表中所處的位置,它得到25。

而c通過引用其名字得到24也就是關鍵字實參的功能。b為預設值5。

第三次調用func(c=50, a=100), 我們隻使用關鍵字實參指定形參值。

注意盡管在函數定義中a比c更早定義,但我們仍然可以先指定c再指定a。

可變參數(VarArgs)

TODO

因為我們還沒有讨論清單和字典,我是不是應該将這部分放到後面的章節?

有時你可能希望編寫一個可以接受任意多個形參的函數,使用星号可以幫你做到:

# Filename: total.py

def total(initial=5, *numbers, **keywords):

    count = initial

    for number in numbers:

        count += number

    for key in keywords:

        count += keywords[key]

    return count

print(total(10, 1, 2, 3, vegetables=50, fruits=100))

    $ python total.py

    166

當我們以星号聲明一個形參比如*param,那麼這個參數點之後的所有實參會被收內建一個清單,

本例中這個清單叫做param。與之類似如果我們以雙星号聲明一個形參,它會被收內建一個關鍵字實參字典。

後面的章節我們會研究清單和字典。

隻能以關鍵字指派的形參(Keyword-only Parameters)

(注:為友善了解和翻譯,以後直接使用英文術語 keyword-only)

如果我們希望某些關鍵字形參隻能通過關鍵字實參得到而不是按照實參的位置得到,可以将其聲明在星号形參後面:

# Filename: keyword_only.py

def total(initial=5, *numbers, vegetables):

    count += vegetables

print(total(10, 1, 2, 3, vegetables=50))

print(total(10, 1, 2, 3))

# 引發錯誤,因為我們沒有為vegetables提供預設實參值

    $ python keyword_only.py

    66

    Traceback (most recent call last):

      File "test.py", line 12, in <module>

    print(total(10, 1, 2, 3))

    TypeError: total() needs keyword-only argument vegetables

在星号形參後面聲明的形參導緻它成為keyword-only實參。

如果沒有為這些實參提供一個預設值,那麼必須在調用函數時以關鍵字實參為其指派,否則将引發錯誤。

如果你隻需要keyword-only實參但不需要星号實參,那麼可以簡單的省略星号實參的實參名。

例如def total(initial=5, *, vegetables)。

return語句

return用于從函數傳回,即跳出函數。也可以利用return語句從函數傳回一個值。

# Filename: func_return.py

def maximum(x, y):

    if x > y:

        return x

        return y

print(maximum(2, 3))

    $ python func_return.py

    3

maximum函數用于傳回參數中的最大值,本例中我們以數字作為參數調用它。

函數使用一個簡單的if…else語句比較出最大值并使用return語句将其傳回。

注意一個不帶有傳回值的return語句相當于傳回return None。

None是python的一個特殊類型,代表空。例如如果一個變量的值為None則代表它不存在值。

每個函數的末尾都隐含的包含一個return None語句除非你編寫了自己的return語句。

你可以通過print(someFunction())證明這點,someFunction是一個沒有顯式使用return語句的函數。例如:

def someFunction():

       pass

其中pass語句用來訓示一個空語句塊。

提示

python已經包含了一個被稱作max的内建函數,它的功能即是尋找最大值,是以盡可能的使用這個函數吧。

DocStrings

python擁有一個俏皮的特性被稱作文檔字元串,通常它被簡稱為docstrings。

文檔字元串是一個你應該利用的重要的工具,因為它幫助你更好的注釋程式使得程式更易于了解。

更神奇的是你甚至可以在程式運作時取得文檔字元串!

# Filename: func_doc.py

def printMax(x, y):

    '''Prints the maximum of two numbers.

    The two values must be integers.'''

    x = int(x) # convert to integers, if possible

    y = int(y)

        print(x, 'is maximum')

        print(y, 'is maximum')

printMax(3, 5)

print(printMax.__doc__)

    $ python func_doc.py

    5 is maximum

    Prints the maximum of two numbers.

            The two values must be integers.

一個函數的第一個邏輯行的字元串将成為這個函數的文檔字元串。

注意類和子產品同樣擁有文檔字元串,在後面相應的章節我們會學到它們。

根據慣例,文檔字元串是一個多行字元串,其中第一行以大寫字母開頭,并以句号結尾。

接下來的第二行為空行,從第三行開始為詳細的描述。

我強烈建議你在你的正規函數中遵循這個編寫文檔字元串的慣例。

我們可以通過使用函數的__doc__屬性(注意雙下劃線)存取printMax的文檔字元串。

記住python中的一切都是對象,其中也包括函數。在後面的類一章我們會學到更多。

如果你在python使用過help(),其實你已經看到過文檔字元串的應用了!

help()隻是取出函數的__doc__屬性,然後以一種整潔的方式顯示給你。

你可以用上面的函數作個實驗 – 在你的程式中包含help(printMax)即可,記住按q鍵退出help。

函數注解(Annotations)

函數還擁有另一個被稱作函數注解的進階特性,對于附加額外的形參和傳回值資訊非常有用。

因為python語言本身并不提供這樣的功能(甩給了第三方庫,具體實作方式第三方庫說了算)。

是以在我們的讨論中決定跳過這一特性。如果你對函數注解有興趣可以參見python增強提議No.3107

小結

我們已經看到了函數的方方面面,但注意這些不是函數的所有方面。

不過現有的關于python函數的知識已經足夠應付日常應用了。

接下來我們将學習如何使用和建立pthon子產品。

--------------Python書籍推薦-----------------

Python基礎教程-第2版.修訂版 

簡明Python3教程 9.函數

PYTHON核心程式設計

簡明Python3教程 9.函數

零基礎學Python

簡明Python3教程 9.函數