函式

函式 在 Python

{other: "%{count} 個練習"}

關於 函式

function是一段有組織、可重複使用的程式碼,用來執行特定的工作。 Functions能讓應用程式有更好的模組化,並大幅提升程式碼的重複使用率。 Python 和其他程式語言一樣,有現成可用的built-in functions(print、map 等等)。 你也可以定義自己的函式,這類函式稱為user-defined functions。 函式可以做的事,從_把訊息印到主控台_這樣簡單的工作,到相當複雜的運算都有。

要執行函式裡的程式碼,你必須呼叫這個函式,做法是在函式名稱後面加上括號[()]。 資料可以放進括號裡傳給函式,這些資料稱為arguments。 函式定義可以包含零個或多個parameters。 參數定義了函式能接受哪些引數。 參數定義了要傳入的引數是什麼_型別_。 引數則是在呼叫函式時實際傳入的_值_。 函式會根據傳入參數的引數不同,執行不同的工作。

函式也可以使用return關鍵字回傳一個值。 函式回傳的值稱為return value。 回傳值會回傳給呼叫這個函式的一方。

建立函式

在 Python 中,函式是使用def關鍵字建立的。 函式定義裡,關鍵字後面接著函式名稱和一對括號[()]。 括號內指定參數,並以逗號分隔。 右括號之後用冒號(:)把函式簽章和函式主體分開。

函式主體是一段程式碼區塊,會在函式被呼叫時執行。 函式主體必須縮排。 縮排很重要,因為 Python 要靠它判斷這段程式碼區塊在哪裡結束。 使用return關鍵字可以從函式回傳一個值,這個值就能讓呼叫函式的一方使用。

def function_name(parameter1, parameter2, ...):
    # function body
    return parameter1 + parameter2

我們也可以定義沒有參數、也沒有回傳值的函式。

def function_name():
    # function body
    pass

要注意的是,函式一定要有主體,即使主體什麼都不做也一樣,否則執行程式時會產生縮排錯誤:

>>> def my_bodyless_func():
...      

File ~/temp.py:1
    
    ^
IndentationError: expected an indented block

呼叫函式

要呼叫函式,就在函式名稱後面加上括號[()]。 傳給函式的參數放在括號內,並以逗號分隔。

看看下面這個函式:

def greet():
    print("Hello")

用下面的語法就能呼叫上面的函式:

>>> greet()
Hello

參數與引數

引數是在呼叫函式時傳入的值。 引數可以是任何資料型態,包括其他函式或類別。

我們來定義一個add函式,把兩個數字相加:

def add(first, second):
    print(first + second)

參數first和second定義了add函式能接受哪些引數。 (要注意,parameter和argument這兩個詞經常被混用,雖然嚴格來說並不精確。) 呼叫函式時,引數會傳進函式裡。 兩個參數都必須傳入引數,否則會引發TypeError。

>>> add(2, 3)
5

# Function can be called multiple times, with different parameters
>>> add(4, 3)
7
>>> add(5, 6)
11

# Passing an incorrect number of parameters will raise a `TypeError`
>>> add(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() missing 1 required positional argument: 'second'

>>> add(2, 3, 4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() takes 2 positional arguments but 3 were given

回傳值

回傳值是函式回傳給呼叫方的值。 return value可以是任何資料型態,包括其他函式或類別。 呼叫方可以用它來做更多運算。 如果函式沒有明確寫出return value,Python 翻譯員會回傳None。

我們來定義一個add函式:

def add(first, second):
    return first + second

我們可以把回傳值存進變數,再把它印出來:

>>> result = add(2, 3)
>>> print(result)
5
# Type of result is `int`
>>> type(result)
<class 'int'>

# We can also perform operations on the return value
>>> result * 2
10

# A function without an explicit return value will return `None`
>>> def log(message):
    print(message)

# Hello is printed because of print(message), but return value is `None`
>>> return_value = log("Hello")
Hello
>>> return_value
None

使用return會立刻結束函式,並把值回傳給呼叫方。

>>> def show(first, second):
    print(first)
    return first
    print(second)

# second never gets printed, because the function exits after the return statement
>>> show(1, 2)
1

模組化

複雜的程式可以拆解成更小的部分。 不同的函式可以用來執行不同的特定工作。

假設一個程式必須完成下列工作:

  • 計算圓的面積
  • 計算長方形的面積
  • 計算三角形的面積

我們可以把程式拆解成更小的部分。

def circle_area(radius):
    return 3.14 * radius * radius

def rectangle_area(length, breadth):
    return length * breadth

def triangle_area(base, height):
    return 0.5 * base * height

現在,我們可以按照想要的順序呼叫這些函式。

>>> circle_area(2)
12.56
>>> triangle_area(2, 3)
3.0
>>> rectangle_area(2, 3)
6
>>> rectangle_area(1, 2) + circle_area(2) + triangle_area(1, 2)
15.56

變數的作用域

如果變數(或_名稱_)定義在函式內部,它就只能在函式作用域(或_區域命名空間_)_內_存取,即使函式作用域外有同名的變數也一樣。

定義在函式外、位於_模組層級_的變數,屬於_全域命名空間_。 定義在函式外,但位於外層函式或類別_內_的變數,屬於_非區域命名空間_。

Python 在程式執行時,會使用 LEGB Rule(Local、Enclosing、Global、Built-in)來解析變數名稱:

  1. 先在區域(或_函式_)命名空間中尋找。
  2. 如果在區域命名空間找不到,就到封閉(或_非區域_)命名空間中尋找。
  3. 如果在非區域/封閉命名空間找不到,就到全域命名空間(模組層級)中尋找。
  4. 如果全域命名空間也找不到,就到內建(程式或整個 Python 範圍)命名空間中尋找。

如果還是找不到,Python 會引發NameError exception。

# Global namespace.
general_favorite = "apples"


def alices_favorite():
   # Local namespace.  
   favorite = "cherries"
   
   # This works because Python will eventually find general_favorite in the global namespace.
   # Python will find 'print' in the built-in namespace.
   print(f'Alice has always liked {favorite}, but most people like {general_favorite}.')

   
def our_favorite_fruits():
    # Enclosing or nonlocal namespace.
    yours = "peaches"
    
    def my_favorite():
        # Local namespace.
        mine = "pears"
        
        # This works because Python will eventually find 'yours' in the enclosing/nonlocal namespace.
        print(f'My favorite is {mine}, but you like {yours} instead.')
        
        # This works because Python will eventually find 'general_favorite' in the global namespace.
        print(f'Everyone seems to like {general_favorite}')
    
    # This function is in the local namespace of the 'our_favorite_fruits' function.  
    my_favorite()

    
# This will raise NameError: name 'favorite' is not defined, because the variable favorite is local to alices_favorite.
print(favorite)

如果想讓變數名稱能在函式區域作用域_外_存取(或是修改定義在函式作用域外的變數),就必須使用global或nonlocal關鍵字。

使用global關鍵字會讓 Python 從_全域命名空間_開始尋找。 這時對變數的指定會修改_全域_變數,而不是建立一個_區域_版本。 用global宣告變數時,這個變數會被_加入_全域命名空間。 因此,global應該謹慎使用,因為新增或修改全域變數可能會影響到所有使用該變數值的其他程式碼。

nonlocal關鍵字會讓 Python 在_非區域或封閉命名空間_中尋找或建立這個變數。 當函式巢狀在另一個函式或類別裡,而且需要存取外層函式的變數和作用域時,就會用到它。

函式是一級物件

在 Python 中,函式可以指定給變數,也可以當成引數傳給其他函式。 函式也可以當成回傳值。 函式也可以放進序列(list、tuple 等)裡,或是當成 dict 的值。 凡是其他物件能使用的地方,函式都能使用。 這是因為_函式本身就是一級物件_。 因此,它們帶有特殊屬性,甚至能自行定義自訂屬性的取得與設定。

# print is a function

# A function can be assigned to a variable.
>>> function_as_variable = print
>>> function_as_variable("Hello")
Hello
>>> type(fake_print)
<class 'builtin_function_or_method'>

# Functions can be passed as an argument to another function.
>>> def check_print(func):
    func("Hello")
>>> check_print(print)
Hello

# Function can be used as an item in a sequence.
>>> my_list = [print, "Hello"]
>>> my_list[0]("Hello")
Hello

# Functions can be used as a value in a dictionary.
>>> my_dict = {"key": print}
>>> my_dict["key"]("Hello")
Hello

# Functions can be returned from a function.
>>> def return_func():
    return print
>>> return_func()("Hello")
Hello

函式也可以巢狀在其他函式裡。

def outer():
    num = 10
    
    # This is a nested, or "inner" function.
    def inner():
        print(num)
    
    # Outer function calling inner function
    inner()

內層函式可以存取定義在外層函式中的變數num。

>>> outer()
10

特殊屬性

Python 的函式有特殊屬性,其中一些是:

  • __name__:函式的名稱
  • __doc__:函式的說明字串
  • __module__:定義該函式的模組
  • __globals__:函式中的全域變數字典
  • __code__:包含函式指令的程式碼物件
>>> def add(first, second):
      return first + second

# Function name
>>> print.__name__
'print'
>>> len.__name__
'len'
>>> add.__name__
'add'

# Function documentation
>>> abs.__doc__
'Return the absolute value of the argument.'

# Module
>>> len.__module__
'builtins'
>>> add.__module__
'__main__'

完整的函式屬性清單可以在 Python DataModel 找到。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟