跳轉到內容

Python 程式設計/函式

來自華夏公益教科書,開放的書籍,開放的世界


函式呼叫

[編輯 | 編輯原始碼]

一個可呼叫物件是一個可以接受一些引數(也稱為引數)並可能返回一個物件(通常是一個包含多個物件的元組)的物件。

函式是 Python 中最簡單的可呼叫物件,但還有其他物件,例如或某些類例項。

定義函式

[編輯 | 編輯原始碼]

函式在 Python 中透過以下格式定義

def functionname(arg1, arg2, ...):
    statement1
    statement2
    ...
>>> def functionname(arg1,arg2):
...     return arg1+arg2
...
>>> t = functionname(24,24) # Result: 48

如果函式不接受任何引數,它仍然必須包含括號,但括號中不包含任何內容

def functionname():
    statement1
    statement2
    ...

函式定義中的引數將函式呼叫(即呼叫函式時)傳遞的引數繫結到函式定義時給定的名稱,這些名稱稱為形式引數。函式內部不知道實際引數的名稱;實際引數的名稱甚至可能不可訪問(它們可能在另一個函式中)。

函式可以“返回”一個值,例如

def square(x):
    return x*x

函式可以在函式體內部定義變數,這些變數被認為是函式的“區域性變數”。區域性變數與引數一起構成函式範圍內的所有變數。函式返回或到達函式體末尾時,函式中的任何名稱都將被解除繫結。

您可以透過以下方式返回多個值

def first2items(list1):
  return list1[0], list1[1]
a, b = first2items(["Hello", "world", "hi", "universe"])
print(a + " " + b)

關鍵詞:返回多個值,多個返回值。

宣告引數

[編輯 | 編輯原始碼]

當呼叫一個函式時,該函式需要一些值進行進一步處理,我們需要將一些值作為函式引數傳送。例如

>>> def find_max(a,b):
   if(a > b):
      return str(a) + " is greater than " + str(b)
   elif(b > a):
      return str(b) + " is greater than " + str(a)
>>> find_max(30, 45)  #Here (30, 45) are the arguments passing for finding max between this two numbers
The output will be: 45 is greater than 30

預設引數值

[編輯 | 編輯原始碼]

如果函式定義中的任何形式引數都以“arg = value”的格式宣告,那麼您在呼叫函式時可以選擇不為這些引數指定值。如果您沒有指定值,那麼該引數將在函式執行時具有給定的預設值。

>>> def display_message(message, truncate_after=4):
...     print(message[:truncate_after])
...
>>> display_message("message")
mess
>>> display_message("message", 6)
messag

連結

可變長度引數列表

[編輯 | 編輯原始碼]

Python 允許您宣告兩個特殊的引數,這些引數允許您建立任意長度的引數列表。這意味著每次您呼叫函式時,您都可以指定超過一定數量的任意數量的引數。

def function(first,second,*remaining):
    statement1
    statement2
    ...

當呼叫上述函式時,您必須為前兩個引數提供值。但是,由於第三個引數用星號標記,因此前兩個引數後的任何實際引數都將被打包成元組並繫結到“remaining”。

>>> def print_tail(first,*tail):
...     print(tail)
...
>>> print_tail(1, 5, 2, "omega")
(5, 2, 'omega')

如果我們宣告一個以兩個星號為字首的形式引數,那麼它將繫結到一個字典,該字典包含實際引數中與任何形式引數不對應的任何關鍵字引數。例如,考慮函式

def make_dictionary(max_length=10, **entries):
    return dict([(key, entries[key]) for i, key in enumerate(entries.keys()) if i < max_length])

如果我們用除 max_length 之外的任何關鍵字引數呼叫此函式,它們將被放入字典“entries”中。如果我們包含 max_length 的關鍵字引數,它將像往常一樣繫結到形式引數 max_length。

>>> make_dictionary(max_length=2, key1=5, key2=7, key3=9)
{'key3': 9, 'key2': 7}

連結

按值傳遞和按引用傳遞

[編輯 | 編輯原始碼]

作為引數傳遞給函式的物件是按引用傳遞的;它們沒有被複制。因此,將大型列表作為引數傳遞不會涉及將所有成員複製到記憶體中的新位置。請注意,即使整數也是物件。但是,按值傳遞按引用傳遞在其他一些程式語言中存在的區別通常用於區分被呼叫函式是否可以實際更改傳遞的引數以及呼叫函式是否可以檢視更改

傳遞的可變型別的物件(例如列表和字典)可以被被呼叫函式更改,並且更改對呼叫函式可見。傳遞的不可變型別的物件(例如整數和字串)不能被被呼叫函式更改;呼叫函式可以確定被呼叫函式不會更改它們。對於可變性,另請參見資料型別章節。

一個例子

def appendItem(ilist, item):
  ilist.append(item) # Modifies ilist in a way visible to the caller

def replaceItems(ilist, newcontentlist):
  del ilist[:]                 # Modification visible to the caller
  ilist.extend(newcontentlist) # Modification visible to the caller
  ilist = [5, 6] # No outside effect; lets the local ilist point to a new list object,
                 # losing the reference to the list object passed as an argument
def clearSet(iset):
  iset.clear()

def tryToTouchAnInteger(iint):
  iint += 1 # No outside effect; lets the local iint to point to a new int object,
            # losing the reference to the int object passed as an argument
  print("iint inside:",iint) # 4 if iint was 3 on function entry 

list1 = [1, 2]
appendItem(list1, 3)
print(list1) # [1, 2, 3]
replaceItems(list1, [3, 4])
print(list1) # [3, 4]
set1 = set([1, 2])
clearSet(set1 )
print(set1) # set([])
int1 = 3
tryToTouchAnInteger(int1)
print(int1) # 3

阻止引數更改

[編輯 | 編輯原始碼]

如果引數是不可變型別,對其進行的任何更改都將保留在被呼叫函式的本地範圍內。但是,如果引數是可變型別,例如列表,對其進行的任何更改都將更新呼叫函式中的相應值。因此,如果呼叫函式想要確保傳遞給某個未知函式的可變值不會被更改,它必須建立並傳遞該值的副本。

一個例子

def evil_get_length(ilist):
  length = len(ilist)
  del ilist[:] # Muhaha: clear the list
  return length

list1 = [1, 2]
print(evil_get_length(list1[:])) # Pass a copy of list1
print(list1) # list1 = [1, 2]
print(evil_get_length(list1)) # list1 gets cleared
print(list1) # list1 = []

呼叫函式

[編輯 | 編輯原始碼]

可以透過將括號中的引數附加到函式名稱或將一對空括號附加到函式名稱來呼叫函式(如果函式不接受任何引數)。

foo()
square(3)
bar(5, x)

可以透過將函式的返回值分配給一個變數來使用它,如下所示

x = foo()
y = bar(5,x)

如上所示,在呼叫函式時,您可以透過名稱指定引數,並且您可以按任何順序這樣做

def display_message(message, start=0, end=4):
   print(message[start:end])

display_message("message", end=3)

上面的內容是有效的,start 將具有預設值 0。對此的限制是,在第一個命名引數之後,其後的所有引數也必須是命名引數。以下是無效的

display_message(end=5, start=1, "my message")

因為第三個引數(“my message”)是未命名的引數。

巢狀函式

[編輯 | 編輯原始碼]

巢狀函式是在其他函式中定義的函式。任意級別的巢狀都是可能的。

巢狀函式可以讀取在外部函式中宣告的變數。對於可變的此類變數,巢狀函式甚至可以修改它們。對於不可變的此類變數(例如整數),在巢狀函式中嘗試修改會導致 UnboundLocalError。在 Python 3 中,可以透過在巢狀函式中宣告一個不可變的外部變數為 nonlocal,類似於 global。完成此操作後,巢狀函式可以為該變數分配一個新值,並且該修改將在巢狀函式之外可見。

巢狀函式可以在 #閉包 中使用,如下所示。此外,它們可以用來減少僅與單個函式相關的程式碼重複,通常由於看到外部變數而減少了引數列表。

修改列表(可變)外部變數的巢狀函式示例

def outside():
  outsideList = [1, 2]
  def nested():
    outsideList.append(3)
  nested()
  print(outsideList)

在巢狀函式定義下方訪問外部變數的示例,仍然有效

def outside():
  def nested():
    outsideList.append(3)
  outsideList = [1, 2]
  nested()
  print(outsideList)

關鍵詞:內部函式、區域性函式。

連結

Lambda 表示式

[編輯 | 編輯原始碼]

lambda 是一個匿名(無名)函式。它主要用於編寫非常短的函式,這些函式以正常方式定義起來很麻煩。像這樣的函式

>>> def add(a, b):
...    return a + b
...
>>> add(4, 3)
7

也可以使用 lambda 定義

>>> print ((lambda a, b: a + b)(4, 3))
7

lambda 通常用作其他函式的引數,這些函式需要函式物件,例如 sorted() 的 'key' 引數。

>>> sorted([[3, 4], [3, 5], [1, 2], [7, 3]], key=lambda x: x[1])
[[1, 2], [7, 3], [3, 4], [3, 5]]

lambda 形式通常用作閉包,例如以下示例所示

>>> def attribution(name):
...    return lambda x: x + ' -- ' + name
...
>>> pp = attribution('John')
>>> pp('Dinner is in the fridge')
'Dinner is in the fridge -- John'

請注意,lambda 函式可以使用建立它的 作用域 中變數的值,類似於上面描述的常規本地定義函式。事實上,匯出由其建構函式體現的預先計算是閉包的重要實用程式之一。

連結

生成器函式

[編輯 | 編輯原始碼]

在討論迴圈時,您遇到了 迭代器 的概念。這依次生成某個序列的每個元素,而不是一次性生成整個序列,允許您處理比一次可以放入記憶體的序列大得多的序列。

您可以透過定義稱為 生成器函式 的函式來建立自己的迭代器。為了說明它的用處,讓我們從考慮一個簡單的函式來返回兩個列表的 連線 開始

def concat(a, b):
    return a + b

print(concat([5, 4, 3], ["a", "b", "c"]))
# prints [5, 4, 3, 'a', 'b', 'c']

想象一下想要做類似 concat(list(range(0, 1000000)), list(range(1000000, 2000000))) 的事情

這會起作用,但會消耗大量的記憶體。

考慮一個替代定義,它將兩個迭代器作為引數

def concat(a, b):
    for i in a:
        yield i
    for i in b:
        yield i

注意使用 yield 語句而不是 return。我們現在可以使用類似這樣的方法

for i in concat(range(0, 1000000), range(1000000, 2000000)):
    print(i)

並打印出大量數字,而根本不使用大量記憶體。

注意:您仍然可以在 Python 需要迭代器的地方(例如 concat 函式的引數)傳遞列表或其他序列型別;這仍然有效,並且可以讓您不必擔心在不需要的地方的區別。

連結

[編輯 | 編輯原始碼]
華夏公益教科書