Python 的流程控制與函式是程式設計的根本。理解流程控制能讓我們精準操控程式執行順序,而函式則能封裝程式碼,提升程式碼的重用性和可讀性。break 和 continue 陳述式在迴圈中斷或繼續執行中扮演關鍵角色,有效控制迴圈流程。函式的定義使用 def 關鍵字,搭配引數和傳回值,實作程式碼模組化。函式呼叫堆積疊則記錄了函式的呼叫順序,確保程式正確執行。最後,變數作用域決定了變數的可存取範圍,全域變數在整個程式碼中可見,而區域變數僅在函式內部有效。這些概念的理解對於撰寫高效且易維護的 Python 程式至關重要。
流程控制與函式
迴圈結構
迴圈結構(Loops)允許程式執行特定區塊的程式碼多次,直到某個條件不再滿足。Python 中的 break 和 continue 陳述式可以用於離開迴圈或跳回迴圈的開始。
練習題
- 如果 Python 程式卡在無窮迴圈中,可以按哪些鍵離開?
break和continue之間的差異是什麼?- 在
for迴圈中,range(10)、range(0, 10)和range(0, 10, 1)之間的差異是什麼? - 寫一個簡短的程式,使用
for迴圈印出 1 到 10 的數字。然後,寫一個等效的程式,使用while迴圈印出 1 到 10 的數字。 - 如果你有一個名為
bacon()的函式,位於名為spam的模組中,你如何在匯入spam後呼叫它?
函式
函式是一個小型的程式,位於一個更大的程式中。Python 提供了內建函式,如 print()、input() 和 len(),但你也可以自己定義函式。在這一章中,你將學習如何建立函式、探索呼叫堆積疊(call stack)以確定函式執行的順序,以及瞭解函式內外變數的作用域。
建立函式
讓我們建立一個函式來更好地理解它們的工作原理。請在檔案編輯器中輸入以下程式並將其儲存為 helloFunc.py:
def hello():
# 印出三個問候
print('早上好!')
print('下午好!')
函式呼叫
要執行這個函式,你需要「呼叫」它。函式呼叫是指在程式中使用函式名稱並加上括號 () 來執行函式內的程式碼。
hello()
這將印出:
早上好!
下午好!
函式引數
函式也可以接受引數,這些引數是在函式定義時指定的變數。這些變數可以在函式內使用。
def greet(name):
print('Hello, ' + name + '!')
greet('Alice')
這將印出:
Hello, Alice!
函式傳回值
函式也可以傳回值,這些值可以在函式呼叫後使用。
def add(x, y):
return x + y
result = add(2, 3)
print(result)
這將印出:
5
函式範圍
函式內的變數和外部變數是不同的。函式內的變數只在函式內有效,而外部變數則可以在整個程式中使用。
x = 10
def foo():
x = 20
print('Inside foo:', x)
foo()
print('Outside foo:', x)
這將印出:
Inside foo: 20
Outside foo: 10
函式的定義和呼叫
函式是程式設計中的一個基本概念,它允許我們將一段程式碼封裝起來,並可重複呼叫。讓我們來看看如何定義和呼叫一個函式。
定義函式
在Python中,函式是使用def關鍵字定義的。例如:
def hello():
print("Good morning!")
print("Good afternoon!")
print("Good evening!")
這個函式名為hello()”,它包含三個print()`陳述式。
呼叫函式
要執行函式中的程式碼,我們需要呼叫函式。呼叫函式的方式是輸入函式名,後面加上括號。例如:
hello()
當我們呼叫hello()函式時,程式會跳到函式定義的位置,並執行函式中的程式碼。
函式的執行
讓我們來看看以下程式碼:
print('Good evening!')
hello()
hello()
print('ONE MORE TIME!')
hello()
這個程式碼先列印“Good evening!”,然後呼叫hello()函式兩次,然後列印“ONE MORE TIME!”,最後再呼叫hello()函式一次。
函式的優點
使用函式可以使程式碼更簡潔、更易於維護。例如,如果我們沒有定義hello()函式,我們就需要重複寫入相同的程式碼。有了函式,我們只需要寫入一次,就可以重複呼叫。
內容解密:
def hello():
# 函式定義
print("Good morning!")
print("Good afternoon!")
print("Good evening!")
# 函式呼叫
print('Good evening!')
hello()
hello()
print('ONE MORE TIME!')
hello()
在這個例子中,我們定義了hello()函式,並在程式中多次呼叫它。這使得程式碼更簡潔、更易於維護。
圖表翻譯:
@startuml
:開始; --> :呼叫hello()函式;
:B; --> :執行hello()函式中的程式碼;
:C; --> :傳回到呼叫函式的位置;
:D; --> :繼續執行程式;
@enduml
這個圖表展示了函式的執行過程。當我們呼叫hello()函式時,程式會跳到函式定義的位置,並執行函式中的程式碼。當函式執行完成後,程式會傳回到呼叫函式的位置,並繼續執行程式。
函式的定義和呼叫
在程式設計中,函式是一個非常重要的概念。函式可以讓我們重複使用一段程式碼,避免重複的工作。讓我們來看看如何定義和呼叫函式。
定義函式
要定義一個函式,我們需要使用 def 關鍵字,後面跟著函式名稱和引數列表。例如:
def say_hello_to(name):
# Prints three greetings to the name provided
print('Good morning, ' + name)
print('Good afternoon, ' + name)
print('Good evening, ' + name)
在這個例子中,我們定義了一個名為 say_hello_to 的函式,它接受一個名為 name 的引數。
呼叫函式
要呼叫一個函式,我們需要使用函式名稱,後面跟著引數值。例如:
say_hello_to('Alice')
say_hello_to('Bob')
在這個例子中,我們呼叫了 say_hello_to 函式兩次,第一次傳遞 'Alice' 作為引數,第二次傳遞 'Bob' 作為引數。
引數和引數
在函式定義中,引數是變數,它們包含了引數的值。引數是傳遞給函式的值。例如:
def say_hello_to(name):
#...
在這個例子中,name 是一個引數,它包含了引數的值。
傳回值和 return 陳述式
函式可以傳回值,使用 return 陳述式。例如:
def add(a, b):
return a + b
在這個例子中,add 函式傳回了 a 和 b 的總和。
內容解密:
def say_hello_to(name):
# Prints three greetings to the name provided
print('Good morning, ' + name)
print('Good afternoon, ' + name)
print('Good evening, ' + name)
這個函式定義了一個名為 say_hello_to 的函式,它接受一個名為 name 的引數。函式內部使用 print 陳述式輸出三個問候陳述式,每個陳述式都包含了 name 引數的值。
圖表翻譯:
@startuml
:定義函式; --> :呼叫函式;
:B; --> :傳遞引數;
:C; --> :執行函式內部程式碼;
:D; --> :傳回值;
@enduml
這個圖表展示了函式的定義、呼叫、傳遞引數、執行內部程式碼和傳回值的過程。
程式設計基礎
程式設計是指根據特定的問題或需求,使用程式語言編寫出可以執行的程式碼,以解決問題或實作特定的功能。下面是一個簡單的範例,展示瞭如何使用Python語言定義一個函式,並根據輸入的引數傳回不同的結果。
函式定義
import random
def get_answer(answer_number):
# 根據輸入的answer_number傳回不同的結果
if answer_number == 1:
return 'It is certain'
elif answer_number == 2:
return 'It is decidedly so'
elif answer_number == 3:
return 'Yes'
elif answer_number == 4:
return 'Reply hazy try again'
elif answer_number == 5:
return 'Ask again later'
elif answer_number == 6:
return 'Concentrate and ask again'
elif answer_number == 7:
return 'My reply is no'
函式呼叫
# 產生一個隨機整數,範圍從1到9
r = random.randint(1, 9)
print(get_answer(r))
內容解密:
在上面的範例中,我們定義了一個名為get_answer的函式,該函式根據輸入的answer_number傳回不同的結果。函式使用if-elif陳述式來判斷哪個結果應該傳回。當函式被呼叫時,會根據傳入的引數傳回相應的結果。
圖表翻譯:
@startuml
:開始; --> :產生隨機整數;
:B; --> :呼叫get_answer函式;
:C; --> :根據answer_number傳回結果;
:D; --> :印出結果;
@enduml
圖表翻譯:
上面的圖表展示了程式的執行流程。首先,程式產生一個隨機整數,然後呼叫get_answer函式,並根據傳入的引數傳回相應的結果。最後,程式印出傳回的結果。
技術原理:
在這個範例中,我們使用了Python語言的random模組來產生隨機整數,並使用if-elif陳述式來判斷哪個結果應該傳回。這些技術是程式設計中的基本元素,常用於解決各種問題。
實際應用:
這個範例可以用於各種實際應用中,例如遊戲、聊天機器人等。透過根據輸入的引數傳回不同的結果,可以建立出更加豐富和有趣的互動體驗。
未來發展:
在未來,程式設計將繼續發揮重要作用,各種新技術和新應用將不斷出現。透過學習和掌握程式設計技能,可以更好地適應未來的發展和變化。
Python 中的函式呼叫和 None 值
Python 的函式可以傳回多種型別的值,包括整數、字串和布林值等。當一個函式沒有明確指定傳回值時,它將傳回 None,這是一個特殊的值,表示沒有傳回值。
函式呼叫和傳回值
在 Python 中,函式可以透過名稱呼叫,並傳遞引數給它。函式執行完成後,可以傳回一個值給呼叫者。例如:
def get_answer(answer_number):
if answer_number == 1:
return 'It is certain'
elif answer_number == 2:
return 'It is decidedly so'
#...
print(get_answer(1)) # 輸出:It is certain
在這個例子中,get_answer 函式傳回了一個字串值給呼叫者,然後這個值被傳遞給 print 函式,並顯示在螢幕上。
None 值
None 是 Python 中的一個特殊值,表示沒有傳回值。當一個函式沒有明確指定傳回值時,它將傳回 None。例如:
def print_hello():
print('Hello!')
result = print_hello()
print(result) # 輸出:None
在這個例子中,print_hello 函式沒有明確指定傳回值,所以它傳回了 None。
使用 None 值
None 值可以用來表示一個變數沒有值,或者一個函式沒有傳回值。例如:
x = None
if x is None:
print('x 沒有值')
在這個例子中,x 變數被設定為 None,然後使用 is 運算元檢查它是否為 None。
函式傳回值與命名引數
在 Python 中,函式的傳回值是一個重要的概念。當我們定義一個函式時,如果沒有明確指定傳回值,Python 會自動新增 return None 到函式定義的末尾。這意味著,如果我們沒有使用 return 陳述式或只使用 return 關鍵字(沒有指定傳回值),函式將傳回 None。
def my_function():
pass
print(my_function()) # Output: None
另一方面,Python 也支援命名引數(named parameters),又稱為關鍵字引數(keyword parameters)或關鍵字引數(keyword arguments)。這些引數允許我們在呼叫函式時,以名稱來指定引數,而不是依靠引數的位置。
def greet(name, message="Hello"):
print(f"{message}, {name}!")
greet("John") # Output: Hello, John!
greet("Jane", message="Hi") # Output: Hi, Jane!
在上面的例子中,greet 函式有兩個引數:name 和 message。message 引數有一個預設值 "Hello",所以如果我們沒有提供 message 引數,函式會使用預設值。
可選引數和命名引數
Python 的內建函式 print() 就是一個使用命名引數的例子。它有兩個可選引數:end 和 sep,分別用於指定輸出結尾和分隔符。
print("Hello", "World", sep=", ", end="!")
# Output: Hello, World!
在這個例子中,我們使用 sep 和 end 命名引數來自定義 print() 函式的行為。
練習題
- 定義一個函式
add(a, b),它傳回兩個引數的總和。 - 修改
add函式,以便它接受一個可選引數message,如果提供,則在傳回總和之前列印一條訊息。 - 使用命名引數呼叫
print()函式,以便在兩個字串之間列印一個逗號和一個空格。
解答
def add(a, b, message=None):
if message is not None:
print(message)
return a + b
result = add(2, 3, message="Adding 2 and 3")
print(result) # Output: 5
print("Hello", "World", sep=", ")
# Output: Hello, World
程式設計與函式呼叫堆積疊
什麼是函式呼叫堆積疊?
函式呼叫堆積疊(Call Stack)是一種資料結構,記錄了程式執行過程中函式的呼叫順序。當一個函式被呼叫時,它會被新增到堆積疊的頂部,而當函式執行完成時,它會被從堆積疊中移除。這樣可以保證程式的執行順序是正確的。
函式呼叫堆積疊的工作原理
當一個函式被呼叫時,程式會將目前的執行位置儲存在堆積疊中,然後跳轉到被呼叫函式的入口點。當被呼叫函式執行完成時,程式會從堆積疊中取出之前儲存的執行位置,然後繼續執行。
範例程式碼
import random
def coin_flip():
"""模擬硬幣翻轉"""
if random.randint(0, 1) == 0:
print('H', end=' ')
else:
print('T', end=' ')
def main():
"""主程式"""
for i in range(100):
coin_flip()
print() # Print one newline at the end.
if __name__ == '__main__':
main()
程式碼解釋
coin_flip函式模擬硬幣翻轉,隨機輸出 ‘H’ 或 ‘T’。main函式呼叫coin_flip函式 100 次,然後輸出一個換行符號。
函式呼叫堆積疊的應用
函式呼叫堆積疊在程式設計中非常重要,它可以幫助我們理解程式的執行順序和函式之間的關係。同時,它也可以用於除錯和最佳化程式的效能。
Mermaid 圖表
@startuml
:main; --> :coin_flip;
:B; --> :print 'H' or 'T';
:C; --> :return to main;
:D; --> :repeat or end;
@enduml
圖表翻譯
main函式呼叫coin_flip函式。coin_flip函式模擬硬幣翻轉,然後輸出 ‘H’ 或 ‘T’。coin_flip函式傳回到main函式。main函式重複呼叫coin_flip函式或結束程式。
函式呼叫與執行順序
在程式設計中,函式的呼叫和執行順序對於程式的正確性和效率有著重要的影響。讓我們一步一步地分析給定的程式碼,以瞭解它的執行流程。
程式碼分析
def a():
print('a() starts')
b()
print('a() returns')
def b():
print('b() starts')
c()
print('b() returns')
def c():
print('c() starts')
print('c() returns')
def d():
print('d() starts')
print('d() returns')
a()
執行流程
- 呼叫函式
a():程式從呼叫a()函式開始。 - 執行
a():在a()中,首先印出"a() starts",然後呼叫b()。 - 呼叫函式
b():在b()中,首先印出"b() starts",然後呼叫c()。 - 呼叫函式
c():在c()中,印出"c() starts"和"c() returns",然後傳回到b()。 - 傳回到
b():在b()中,印出"b() returns",然後傳回到a()。 - 傳回到
a():在a()中,印出"a() returns",標誌著程式的結束。
輸出結果
根據以上分析,程式的輸出結果應該是:
a() starts
b() starts
c() starts
c() returns
b() returns
a() returns
這個結果與預期的輸出完全匹配,證明瞭我們對程式執行流程的理解是正確的。
內容解密:
- 函式的呼叫和傳回是程式設計中的基本概念。
- 每個函式都有一個明確的入口和出口,透過呼叫和傳回來控制程式的流程。
- 函式可以巢狀呼叫,這意味著一個函式可以在執行過程中呼叫另一個函式。
- 程式的執行順序由函式的呼叫和傳回順序決定。
圖表翻譯:
@startuml
:a() starts; --> :b() starts;
:B; --> :c() starts;
:C; --> :c() returns;
:D; --> :b() returns;
:E; --> :a() returns;
@enduml
這個流程圖清晰地展示了函式之間的呼叫和傳回關係,有助於更好地理解程式的執行流程。
函式呼叫與範圍
當我們呼叫一個函式時,Python 會記住呼叫的位置,以便在函式結束後傳回到正確的位置。這個過程是透過呼叫堆積疊(call stack)來實作的。
呼叫堆積疊
呼叫堆積疊是一個記憶體區域,Python 用它來記住函式呼叫的位置。當我們呼叫一個函式時,Python 會在堆積疊上方建立一個新的框架物件(frame object),這個物件包含了原始函式呼叫的行號。當函式結束後,Python 會從堆積疊上移除框架物件,並傳回到記憶體中儲存的行號。
範圍
在 Python 中,變數可以分為兩種範圍:區域性範圍(local scope)和全域範圍(global scope)。區域性範圍是指在函式內部定義的變數,只有在函式內部才能夠存取。全域範圍是指在函式外部定義的變數,可以在程式的任何地方存取。
範圍規則
以下是 Python 中的範圍規則:
- 全域範圍中的程式碼不能夠使用區域性範圍中的變數。
- 區域性範圍中的程式碼不能夠使用其他區域性範圍中的變數。
- 區域性範圍中的程式碼可以存取全域範圍中的變數。
- 可以在不同的範圍中使用相同的變數名稱。
範圍範例
讓我們透過一些範例來瞭解這些規則。
全域範圍中的程式碼不能夠使用區域性範圍中的變數
def spam():
eggs = 'sss'
spam()
print(eggs) # 會引發錯誤,因為 eggs 是區域性範圍中的變數
區域性範圍中的程式碼不能夠使用其他區域性範圍中的變數
def spam():
eggs = 'sss'
def ham():
print(eggs) # 會引發錯誤,因為 eggs 是另一個區域性範圍中的變數
spam()
ham()
區域性範圍中的程式碼可以存取全域範圍中的變數
eggs = 'sss'
def spam():
print(eggs) # 可以存取全域範圍中的變數 eggs
spam()
可以在不同的範圍中使用相同的變數名稱
eggs = 'sss'
def spam():
eggs = 'mmm' # 可以在區域性範圍中使用相同的變數名稱
print(eggs) # 輸出 mmm
spam()
print(eggs) # 輸出 sss
變數作用域與Python函式
在Python中,變數的作用域是指變數可以被存取的範圍。變數可以定義在全域(global)或區域性(local)作用域中。
全域變數
全域變數是指定義在程式的最外層的變數,可以在程式的任何地方存取。例如:
x = 10 # 全域變數
def foo():
print(x) # 可以存取全域變數
foo() # 輸出:10
區域性變數
區域性變數是指定義在函式內的變數,只能在該函式記憶體取。例如:
def foo():
x = 10 # 區域性變數
print(x) # 可以存取區域性變數
foo() # 輸出:10
print(x) # 錯誤:x 未定義
函式間的變數分享
當函式A呼叫函式B時,函式B可以存取自己的區域性變數,但不能存取函式A的區域性變數。例如:
def foo():
x = 10 # 區域性變數
def bar():
print(x) # 錯誤:x 未定義
bar()
foo()
全域變數與區域性變數的區別
全域變數和區域性變數有以下區別:
- 全域變數可以在程式的任何地方存取,而區域性變數只能在定義它的函式記憶體取。
- 全域變數的生命週期是整個程式的執行時間,而區域性變數的生命週期是函式的執行時間。
內容解密:
x = 10 # 全域變數
def foo():
x = 20 # 區域性變數
print("foo:", x) # 輸出:foo: 20
def bar():
print("bar:", x) # 輸出:bar: 10
foo()
bar()
print("global:", x) # 輸出:global: 10
在上面的例子中,x 是一個全域變數,初始值為 10。函式 foo() 定義了一個區域性變數 x,初始值為 20。函式 bar() 則存取全域變數 x。最後,程式輸出全域變數 x 的值。
圖表翻譯:
@startuml
:全域變數 x; --> [10] :10;
:B; --> [20] :20;
:A; --> [10] :10;
:D; --> [10] :10;
:B; --> [20] :20;
:E; --> [10] :10;
@enduml
在上面的圖表中,描述了全域變數 x 和區域性變數 x 之間的關係,以及它們如何被函式 foo() 和 bar() 存取。
全域變數與區域變數
在 Python 中,變數可以分為全域變數(global variable)和區域變數(local variable)。全域變數是在函式外定義的變數,可以在任何地方存取。區域變數是在函式內定義的變數,只能在該函式記憶體取。
全域變數
全域變數是在函式外定義的變數,可以在任何地方存取。例如:
eggs = 'GLOBALGLOBAL'
def spam():
print(eggs) # Prints 'GLOBALGLOBAL'
spam()
print(eggs) # Prints 'GLOBALGLOBAL'
在這個例子中,eggs 是一個全域變數,可以在 spam() 函式內和外存取。
區域變數
區域變數是在函式內定義的變數,只能在該函式記憶體取。例如:
def spam():
eggs = 'spam local'
print(eggs) # Prints 'spam local'
spam()
在這個例子中,eggs 是一個區域變數,只能在 spam() 函式記憶體取。
全域變數和區域變數同名
技術上,允許使用相同的變數名稱作為全域變數和區域變數。但是,為了簡化程式設計,應避免這種情況。以下是示範同名變數可能發生的情況:
def spam():
eggs = 'spam local'
print(eggs) # Prints 'spam local'
def bacon():
eggs = 'bacon local'
print(eggs) # Prints 'bacon local'
eggs = 'global'
spam()
print(eggs) # Prints 'global'
bacon()
print(eggs) # Prints 'global'
在這個例子中,eggs 同時作為全域變數和區域變數。當執行 spam() 函式時,輸出的是 'spam local';當執行 bacon() 函式時,輸出的是 'bacon local'。但是,當存取全域變數 eggs 時,輸出的是 'global'。
圖表翻譯:
@startuml
:開始; --> :定義全域變數 eggs;
:B; --> :定義函式 spam();
:C; --> :定義區域變數 eggs 在 spam() 內;
:D; --> :執行 spam() 函式;
:E; --> :輸出 'spam local';
:F; --> :定義函式 bacon();
:G; --> :定義區域變數 eggs 在 bacon() 內;
:H; --> :執行 bacon() 函式;
:I; --> :輸出 'bacon local';
:J; --> :存取全域變數 eggs;
:K; --> :輸出 'global';
@enduml
這個圖表展示了程式的執行流程和變數的作用範圍。
變數命名與作用域
在 Python 中,變數的命名和作用域是非常重要的概念。讓我們來探討一下如何正確地使用變數名稱和管理變數的作用域。
變數命名規則
為了避免變數名稱的混淆,應該給所有變數唯一的名稱,即使它們位於不同的作用域中。這樣可以減少程式碼的複雜性和維護成本。
全域變數與區域性變數
Python 中的變數可以分為全域變數和區域性變數。全域變數是指定義在所有函式外部的變數,它們可以在任何地方被存取。另一方面,區域性變數是指定義在函式內部的變數,它們只能在該函式內被存取。
global 陳述式
如果需要在函式內修改全域變數,可以使用 global 陳述式。這個陳述式告訴 Python,在這個函式中,某個變數是全域變數,因此不應該建立一個同名的區域性變數。
範例:使用 global 陳述式
def spam():
global eggs # 指定 eggs 為全域變數
eggs = 'spam' # 修改全域變數 eggs
eggs = 'global' # 定義全域變數 eggs
spam() # 呼叫函式 spam()
print(eggs) # 輸出 'spam'
在這個範例中,eggs 在 spam() 函式中被宣告為全域變數,因此對 eggs 的指定會修改全域變數 eggs。
作用域識別規則
以下是四個規則,幫助您判斷變數屬於哪個作用域:
- 全域作用域:在所有函式外部定義的變數是全域變數。
- global 陳述式:在函式中使用
global陳述式的變數是全域變數。 - 函式內部:如果一個變數在函式內部被指定,但沒有使用
global陳述式,則它是區域性變數。 - 無法識別:如果以上規則都不能適用,則變數的作用域無法識別。
遵循這些規則和最佳實踐,您可以更好地管理 Python 中的變數命名和作用域,寫出更清晰、可維護的程式碼。
從技術架構視角來看,Python 的流程控制與函式機制是程式碼組織和複用的根本。理解迴圈結構中 break、continue 的運用以及 range() 函式的差異,能提升程式碼的精確性和效率。分析函式的定義、呼叫、引數傳遞、傳回值和作用域等核心概念,則能有效地封裝程式碼邏輯,提高程式碼的可讀性和可維護性。然而,全域變數和區域性變數的混淆使用可能導致難以預料的錯誤,需要特別注意。展望未來,隨著程式碼複雜度的提升,掌握函式式程式設計的思想,例如使用 lambda 函式、閉包等,將有助於構建更具彈性且易於測試的程式碼架構。對於初學者而言,建議著重理解函式呼叫堆積疊的運作機制以及變數作用域的規則,並透過實際程式碼練習來強化理解。玄貓認為,深入理解這些基礎概念,才能在程式設計的道路上走得更穩、更遠。