Python if for function lambda args

[Python] 流程控制與函式

資料結構搞定後,這篇把流程控制(if / match / for / while)跟函式(預設值、*args/*、closure、lambda)一次串完。重點放在 Python 特有的設計:可變預設值的陷阱、限定參數種類的 /*if __name__ == '__main__' 這個慣用 pattern。

if / elif / else

n = 5
if n > 0:
    print('positive')
elif n == 0:
    print('zero')
else:
    print('negative')

三元表達式:

label = 'positive' if n > 0 else 'non-positive'

match(Python 3.10+)

結構化的 pattern matching:

def describe(x):
    match x:
        case 0:
            return 'zero'
        case int() if x < 0:
            return 'negative'
        case [a, b]:
            return f'pair {a},{b}'
        case {'kind': 'circle', 'r': r}:
            return f'circle r={r}'
        case _:
            return 'other'

不只能比對值,還能解構 list / dict、判斷型別、加 guard 條件。

for 與 while

for 走訪可迭代物件:

for x in [1, 2, 3]:
    print(x)

# range
for i in range(5):           # 0..4
    print(i)

for i in range(2, 10, 2):    # 2, 4, 6, 8
    print(i)

# 同時拿 index
for i, v in enumerate(['a', 'b', 'c']):
    print(i, v)

# 同時走訪多個
for a, b in zip([1, 2, 3], ['a', 'b', 'c']):
    print(a, b)

# 反向
for x in reversed([1, 2, 3]):
    print(x)

while

n = 3
while n > 0:
    print(n)
    n -= 1

break / continue

for x in range(10):
    if x == 3:
        continue
    if x == 7:
        break
    print(x)

for / while 的 else

迴圈正常結束(沒有被 break)才會執行 else

for x in [1, 2, 3]:
    if x == 99:
        break
else:
    print('not found')   # 會印

實務不太常用,看到不要嚇到。

函式

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

# 預設值
def greet(name, greeting='Hello'):
    return f'{greeting}, {name}'

greet('Jeremy')
greet('Jeremy', 'Hi')
greet(name='Jeremy', greeting='Hi')   # 具名參數
greet(greeting='Hi', name='Jeremy')   # 順序可換

預設值「定義時就建立並共用」,所以可變物件當預設值會踩雷

def append(item, lst=[]):     # ❌ 同一個 list 被所有呼叫共用
    lst.append(item)
    return lst

append(1)   # [1]
append(2)   # [1, 2]  ← 不是 [2]!

正確寫法:用 None 哨兵:

def append(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

*args 與 **kwargs

收集任意數量的位置參數與關鍵字參數:

def f(*args, **kwargs):
    print(args)     # tuple
    print(kwargs)   # dict

f(1, 2, 3, name='Jeremy', age=30)
# args = (1, 2, 3)
# kwargs = {'name': 'Jeremy', 'age': 30}

呼叫時也能用 * / ** 拆包:

nums = [1, 2, 3]
def add3(a, b, c): return a + b + c
add3(*nums)     # 等同 add3(1, 2, 3)

opts = {'name': 'Jeremy', 'age': 30}
def show(name, age): print(name, age)
show(**opts)

限定參數種類:/ 與 *

def f(a, b, /, c, d, *, e, f):
    pass
#       ^         ^
#       /         *
# a, b 必須位置傳;c, d 兩種都行;e, f 必須具名傳

f(1, 2, 3, 4, e=5, f=6)            # ✅
f(1, 2, c=3, d=4, e=5, f=6)        # ✅
f(a=1, b=2, c=3, d=4, e=5, f=6)    # ❌ a, b 不能具名
f(1, 2, 3, 4, 5, 6)                # ❌ e, f 不能位置傳

設計對外 API 時很有用:把容易搞混的參數鎖成只能具名傳。

return

沒寫 return 或寫 return 不帶值,預設回傳 None

def noop():
    pass

print(noop())   # None

回傳多個值就 tuple:

def split(s):
    return s.split('@')

local, domain = split('a@b.com')

lambda(匿名函式)

只能寫一個表達式:

square = lambda x: x * x
square(3)   # 9

sorted([(1, 'b'), (3, 'a'), (2, 'c')], key=lambda x: x[1])
# [(3, 'a'), (1, 'b'), (2, 'c')]

只在「需要短小函式作為值」的場景用(sort key、map / filter)。複雜邏輯一律寫 def

函式是一等公民

def add(a, b): return a + b
def mul(a, b): return a * b

ops = {'+': add, '*': mul}
ops['+'](2, 3)   # 5

# 傳給其他函式
def apply(fn, x, y):
    return fn(x, y)

apply(add, 1, 2)

巢狀函式與 closure

def make_counter():
    n = 0
    def inc():
        nonlocal n     # 不寫 nonlocal 會被當成新區域變數
        n += 1
        return n
    return inc

c = make_counter()
c()   # 1
c()   # 2

nonlocal 指向外層(非 global)作用域,global 指向最外層全域作用域。

沒有 nonlocal 會怎樣?n += 1 等同於 n = n + 1,Python 看到賦值就把 n 當成「新的區域變數」,但右側讀 n 時還沒定義——觸發 UnboundLocalErrornonlocal 明確告訴 Python「這個 n 是外層作用域的那個」。

docstring

函式第一行寫字串就會變成 docstring,被 help() 讀取:

def add(a, b):
    """Return the sum of a and b."""
    return a + b

help(add)

一個常見的 main pattern

def main():
    # 真正的程式邏輯
    ...

if __name__ == '__main__':
    main()

這個 .py直接執行時,__name__'__main__';被 import 時則是模組名。所以這段程式只在直接執行時才跑,被 import 時不會。

走 functional 風格

# map / filter(會回傳 iterator)
list(map(lambda x: x * 2, [1, 2, 3]))            # [2, 4, 6]
list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4])) # [2, 4]

# 等價的 comprehension(更 pythonic)
[x * 2 for x in [1, 2, 3]]
[x for x in [1, 2, 3, 4] if x % 2 == 0]

# reduce 要 import
from functools import reduce
reduce(lambda a, b: a + b, [1, 2, 3, 4])   # 10
# 累積過程:a=1, b=2 → 3;a=3, b=3 → 6;a=6, b=4 → 10

慣例:能寫 comprehension 就寫 comprehension,可讀性比 map / filter 好。

下一篇進物件導向,這篇的函式概念會延伸到 method、self、classmethod 與 staticmethod,順便把 dataclass 一次帶過。

Latest Updates

  • 2026.06.11 Content updated