Python入門-函式

DeanWu發表於2019-02-16

< 返回索引頁

<!– /TOC –>

函式

在維基百科上函式式這樣描述的:

函式在數學中為兩集合間的一種對應關係:輸入值集合中的每項元素皆能對應唯一一項輸出值集合中的元素。

此處的函式區別於我們數學上的函式,在程式設計世界中,函式(Functions)是指可重複使用的程式片段。它們允許你為某個程式碼塊賦予名字,允許你
通過這一特殊的名字在你的程式任何地方來執行程式碼塊,並可重複任何次數。這就是所謂的呼叫函式。我們已經使用過了許多內建的函式,例如 lenrange

基本結構如下:

def 函式名(引數列表):
    " 函式說明 "
    函式體

函式定義

在Python中,使用關鍵字def來定義一個函式。結構如下:

def hello(name):  
    " 返回字串"
    return name
    
def hello2():
    pass 

語法說明:

  • 函式的引數支援多種形式
  • 函式說明,可通過內部方法hello.__doc__ 獲取
  • 函式的返回值支援返回多個
  • 我們可以使用 pass 來定義個空函式

下面來展開一一講述。

返回值

函式中返回值,使用關鍵字return 來表示,返回值可以是任意型別,當沒有return語句時函式返回None。函式返回值支援多個,返回多個時,使用逗號分隔。如下:

def say_hello(name):
    print(name)
    return `Hello, %s` % name, name

函式的呼叫

函式的呼叫,上邊我們已經使用了很多次了,函式名加上需要傳入的響應引數即可。這裡需要注意的是,在python中,一切結尾物件,當然函式也是物件。函式名也可以作為引數傳遞到函式中使用。

def say_hello(name):
    print(`hello, %s` % name)

def wapper(fun):
    print(`呼叫之前做一些事`)
    fun(`Dean`)
    print(`呼叫之後做一些事`)

wapper(say_hello)
"""
呼叫之前做一些事
hello, Dean
呼叫之後做一些事
"""

函式的引數

在程式設計中函式引數有2個比較通用的概念,形參和實參,在各高階語言中都會有。形參,即形式上的引數,不佔用記憶體空間。實參,是實際真正的引數。

看下面這個例項:

def say_hello(name):  
    print(`hello, %s` % name )
say_hello(`Dean`)

其中,函式引數name即為函式的形參。我們呼叫函式時,傳入的引數Dean便是實參。在Python程式設計時,這個概念並不是那麼重要,此種瞭解即可。

順序引數

在Python 函式中,多個引數按照一定的順序依次傳入函式,其值依次對應賦給函式宣告時引數,這種引數叫順序引數

def say_hello(name, age):
    print(`hello, %s` % name)
    print(`you age: %s ` % age)

say_hello(`Dean`, 30)

如上,Dean與30 這2個引數按照由左到右依次賦值給形參nameage供函式內部使用。需要注意的是,順序引數中,形參和實參的個數需要一直,否則函式會報呼叫錯誤。

引數的預設值

在函式宣告時,可以給引數設定預設值,如下:

def say_hello(name=`Tim`, age=20):
    print(`hello, %s` % name)
    print(`you age: %s ` % age)

say_hello(`Dean`, 30)
say_hello(`Dean`)

當函式引數設定預設值後,該引數在呼叫函式時,可以省略,省略時則會使用預設值。有此可以退出,引數沒有設定預設值,則是必傳的。

可變引數

有時候我們在宣告函式時,沒法確定函式的引數個數,或者我們本身就想設計一種動態的引數,這種設計是否可以滿足呢?Python的函式對這種情況已做了考慮,提供了可變引數來滿足這種需求。

def say_hello(*args):
    print(args)
    for name in args:
        print(`hello, %s` % name)

say_hello(`Dean`,`Tim`)
(`Dean`, `Tim`)
hello, Dean
hello, Tim

可變引數使用*加一個形參來表示,其值為一個元組型別,元組內的元素為我們傳入的實參。

關鍵字引數

在可變引數中,我們傳入函式的引數只能按順序或著通過下標來獲取,那麼可不可以給引數起一個名字呢,答案是可以的。Python 為這種情況提供了一種引數形式,叫做關鍵字引數

def say_hello(**kwargs):
        print(`hello, %s` % kwargs[`name`])
        print(`you age: %s` % kwargs[`age`])

say_hello(name=`Dean`, age=30)

使用**加一個形參來表示,其值為一個字典,key為我們實參中傳入的變數,value值為實參中的變數的值。

引數的組合

Python 函式中,以上引數可組合使用。當引數組合使用時,需要注意以下原則:

  • 有預設值的引數需要放在無預設值引數的後邊
  • 當順序引數和可變引數混合使用時,可變引數的取值為超出順序引數的實參部分,且可變引數在順序引數之後。
  • args 和 *kwargs 是 Python 的慣用寫法,變數名可以更換。

引數的常用排列順序為,無預設值順序引數、有預設值順序引數、可變引數、關鍵字引數。

def say_hello(name, age=30, *args, **kwargs):
    print(args)
    print(kwargs)

    print(`hello, %s` % name)
    print(`you age: %s` % age)
    print(`address: %s` % kwargs[`address`])

say_hello(`Dean`, 20, address=`Beijing`)

引數的專遞

我們在呼叫函式時,將實參傳遞給了函式的形參,供函式中邏輯程式碼使用。那麼在這個過程中,計算機記憶體是一個怎麼的過程呢?我們在學習變數時知道,Python中的變數是其內部儲存值的一個記憶體地址或者叫做一個引用。在傳入函式時,便是將這個引用傳入了函式。那麼我們在函式內部修改變數的值時,是不是就修改了外部的變數的值呢?答案是不一定。

在我們學習基本資料結構時,我們知道有可變型別(字典、列表、集合)和不可變型別(數值、字串、元組)。當我們傳入函式的引數為可變型別時,函式中的形參使用的記憶體地址同實參的記憶體地址,當改變形參的值時,實參的值也相應跟著修改了。當我們傳入的為不可變數時,形參會重新分配一塊記憶體地址儲存實參傳給他的值,當形參修改時,實參自然不會修改。

來看下邊的例子:

# func.py 
def say_hello1(name):
    `不可變型別引數`
    name = `Time`
    print(`name: %s` % name )


def say_hello2(user_dict):
    `可變型別引數`
    user_dict[`name`] =`Time`
    print(`name: %s` % user_dict[`name`])

name = `Dean`
print(`name before:`, name)
say_hello1(name)
print(`name after:`, name)

user_dict = {`name`:`Dean`}
print(`user_dict before:`, user_dict)
say_hello2(user_dict)
print(`user_dict after:`, user_dict)

"""
# 不可變引數,值沒有變
(`name before:`, `Dean`)
name: Time
(`name after:`, `Dean`)

# 可變引數,值變了
(`user_dict before:`, {`name`: `Dean`})
name: Time
(`user_dict after:`, {`name`: `Time`})
"""

函式作用域

在第三章,我們學習變數時,知道變數的作用域決定了在哪一部分程式可以訪問哪個特定的變數名稱。在Python中,函式會重新開啟一個作用域,當在函式內部定義的變數,在函式外部是無法訪問的。那麼該變數便是函式的區域性變數,函式外部的變數便可成為全域性變數

total = 0 # 這是一個全域性變數
# 可寫函式說明
def sum( arg1, arg2 ):
    #返回2個引數的和.
    total = arg1 + arg2 # total在這裡是區域性變數.
    print ("函式內是區域性變數 : ", total)
    return total

#呼叫sum函式
sum( 10, 20 )
print ("函式外是全域性變數 : ", total)

那麼當內部的區域性變數想要暴露出來給外部使用時如何實現呢?Python提供了2個關鍵字,gobalnonlocal

global 將變數作用域變為函式外層全域性作用域。

num = 1
def fun1():
    global num  # 需要使用 global 關鍵字宣告
    print(num) 
    num = 123
    print(num)
fun1()

nonlocal 將變數作用域變為函式外部非全域性變數,及閉包(Enclosing)作用域。

def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal關鍵字宣告
        num = 100
        print(num)
    inner()
    print(num)
outer()

內建函式

內建函式,是Python中已經定義好的函式可以在編寫程式碼時直接使用。我們已經使用過內建函式,如lenrange 等。更多的內建函式,可參閱官方文件中函式說明

匿名函式

匿名函式,就是不使用def來定義的函式。Python中使用關鍵字lambda 來實現匿名函式。

# 語法
lambda [arg1 [,arg2,.....argn]]:表示式

語法說明:

  • arg為引數,可以任意多個,使用逗號分隔
  • 表示式 為函式體,使用冒號開頭
  • 表示式 的返回值即為函式的返回值

例項:

# 內名函式
sum = lambda arg1, arg2: arg1 + arg2
 
# 呼叫sum函式
print ("相加後的值為 : ", sum( 10, 20 ))
print ("相加後的值為 : ", sum( 20, 20 ))

函式註解

函式註解,為函式和引數增加了一些說明特性。python3.0 新增的特性,見PEP3107。可通過函式的__annotations__ 熟悉檢視。

def f(ham: 42, eggs: int = `spam`) -> "Nothing to see here":
    print("Annotations:", f.__annotations__)
    print("Arguments:", ham, eggs)

f(`wonderful`)
```
Annotations: {`eggs`: <class `int`>, `return`: `Nothing to see here`, `ham`: 42}
Arguments: wonderful spam
```

def foo(a: `x`, b: 5 + 6, c: list) -> max(2, 9):
    pass 

語法說明:

  • 引數的說明在引數後邊,使用冒號和引數隔離,註釋為一個表示式。
  • 函式的註解在函式引數括號後,使用`->` 符號開頭,也是一個表示式。

在Python的程式設計時,註解使用的比較少,以方便人們更多的使用docstring來說明引數及返回值,另一方面Python函式引數可進一步通過編碼規範一眼就能看出什麼型別。這種註釋語法,反而顯得囉嗦冗餘。

總結

本章我們學習了函式及其使用,知識重點總結如下:

  • Python提供了許多內建函式共我們使用,內建函式可直接使用無需定義。
  • 引數組合在使用的時候是有順序的,依次是無預設值順序引數、有預設值順序引數、可變引數、關鍵字引數。
  • args 表示可變引數,*kwargs 表示關鍵字引數。
  • 當引數為不可變引數時,函式不會影響外部實參值,當引數為不可變引數時,函式則會影響外部實參值。
  • 函式本身可作為引數傳遞。
  • Python 中使用lambda 來實現匿名函式。

練習

  • 1、求n的階乘
def factorial(num):
    """
    求階乘
    :param num: 非負整數
    :return: num的階乘
    """
    result = 1
    for n in range(1, num + 1):
        result *= n
    return result

n = int(input(`n = `))
factorial(n)
  • 2、實現判斷一個數是不是素數的函式。
# 素數,在大於1的自然數中,除了1和它本身以外不再有其他因數
def is_prime(num):
    for factor in range(2, num):
        if num % factor == 0:
            return False
    return True if num != 1 else False

參考

相關文章