← 返回大綱
第五章

串列、元組
與字典處理

Lists, Tuples & Dictionaries

用撲克牌建出完整的牌組和遊戲!

概念

四種容器,各有用途

Python 提供不同的「容器」來存放多筆資料,就像生活中不同的收納工具:

list [ ] — 書架

有順序、可以隨時抽出或插入書。
像你的手牌,可以加牌、出牌、重排。

tuple ( ) — 密封信封

裝好就不能改,保證資料不被動到。
像一張牌的(花色, 牌面),印好就不會變。

dict { } — 電話簿

用名字查電話,用鍵查值。
牌面 → 點數的對照表。

set { } — 彈珠袋

丟進去自動去除重複,沒有順序。
像檢查「有哪些不同的花色」。

Scratch → Python

清單 → list:一副牌

Scratch
清單 hand
Python
hand = ["A", "7", "K"]
# 一副牌的 13 個牌面
ranks = ["A", "2", "3", "4", "5", "6", "7",
         "8", "9", "10", "J", "Q", "K"]

print(ranks[0])       # A(索引從 0 開始)
print(ranks[-1])      # K(-1 是最後一個)
print(ranks[0:3])     # ['A', '2', '3'](切片)
print(len(ranks))     # 13(長度)
索引從 0 開始!

第一張牌是 [0],第二張是 [1],以此類推。

list 方法

手牌操作:新增、移除、排序

Scratch
將 "K" 加入 hand
刪除 hand 第 1 項
hand 的項目數
Python
hand.append("K")
hand.pop(0)
len(hand)
hand = ["7", "A", "3"]

hand.append("K")         # 加到末尾 → ['7','A','3','K']
hand.insert(0, "2")      # 插入到最前面
hand.remove("3")         # 移除指定的牌
popped = hand.pop()      # 移除並回傳最後一張
hand.sort()              # 排序
hand.reverse()           # 反轉順序
print("A" in hand)       # True/False — 手上有沒有 A?
語法

list 語法速查表

操作語法說明
建立hand = ["A", "7", "K"][ ] 包住,逗號隔開
取值hand[0]索引從 0 開始
切片hand[1:3]取索引 1 到 2(不含 3)
長度len(hand)有幾個元素
加入hand.append("Q")加到末尾
插入hand.insert(0, "2")插入指定位置
移除hand.remove("7")移除指定值(第一個)
抽出hand.pop()移除並回傳最後一個
排序hand.sort()由小到大排列
檢查"A" in hand是否包含某值
list

for 走訪手牌

hand = ["A♠", "7♥", "K♦", "3♣", "10♠"]

# 方法 1:直接走訪每張牌
for card in hand:
    print(card)

# 方法 2:用 enumerate 同時取得索引和值
for i, card in enumerate(hand):
    print(f"第 {i+1} 張:{card}")

# 方法 3:用 range + 索引
for i in range(len(hand)):
    print(f"hand[{i}] = {hand[i]}")
常見寫法

不需要索引 → 用 for card in hand
需要索引 → 用 for i, card in enumerate(hand)

list

List Comprehension 串列推導式

一行就能產生或過濾 list:

# 傳統寫法:產生一副牌
suits = ["♠", "♥", "♦", "♣"]
ranks = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"]
deck = []
for s in suits:
    for r in ranks:
        deck.append(r + s)

# List Comprehension:一行搞定!
deck = [r + s for s in suits for r in ranks]
print(len(deck))    # 52
print(deck[:5])     # ['A♠', '2♠', '3♠', '4♠', '5♠']

# 過濾:只要黑桃
spades = [card for card in deck if "♠" in card]
print(spades)       # ['A♠', '2♠', ..., 'K♠']
list + 函式

洗牌與發牌

import random

def create_deck():
    """建立一副 52 張的撲克牌"""
    suits = ["♠", "♥", "♦", "♣"]
    ranks = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"]
    return [r + s for s in suits for r in ranks]

def deal(deck, n=5):
    """從牌堆抽 n 張牌(會從牌堆移除)"""
    hand = []
    for i in range(n):
        card = deck.pop()    # 從牌堆頂抽一張
        hand.append(card)
    return hand

# --- 使用 ---
deck = create_deck()
random.shuffle(deck)        # 洗牌(就地打亂)
print(f"牌堆共 {len(deck)} 張")

my_hand = deal(deck, 5)
print(f"你的手牌:{my_hand}")
print(f"牌堆剩 {len(deck)} 張")
元組

Tuple — 不可修改的組合

一張牌可以用 (花色, 牌面) 的 tuple 表示:

# tuple 用小括號
card = ("♠", "A")
print(card[0])     # ♠
print(card[1])     # A

# tuple 不能修改!
# card[0] = "♥"   # ❌ TypeError!

# 用 tuple 建牌組
deck = [("♠","A"), ("♥","K"), ("♦","7"), ("♣","3")]
for suit, rank in deck:
    print(f"{rank}{suit}")

list [ ]

  • 可以修改(增刪改)
  • 適合:手牌、牌堆

tuple ( )

  • 不可修改(唯讀)
  • 適合:一張牌(花色, 點數)
字典

Dict — 牌面對應點數

用「鍵」查找「值」,就像查字典:

# 每張牌面對應幾點
card_values = {
    "A": 14, "K": 13, "Q": 12, "J": 11,
    "10": 10, "9": 9, "8": 8, "7": 7,
    "6": 6, "5": 5, "4": 4, "3": 3, "2": 2
}

print(card_values["A"])      # 14
print(card_values["7"])      # 7
print(card_values.get("Joker", 0))  # 0(找不到時的預設值)
dict vs list

list:用數字索引 [0], [1], [2]
dict:用有意義的鍵 ["A"], ["K"],語意更清楚

字典

字典常用操作:玩家記分板

scores = {"小明": 0, "小華": 0}

# 新增 / 修改
scores["小美"] = 0          # 新增玩家
scores["小明"] = 10         # 小明加分

# 刪除
del scores["小美"]

# 遍歷
for player, score in scores.items():
    print(f"{player}:{score} 分")

# 常用方法
print(scores.keys())     # dict_keys(['小明', '小華'])
print(scores.values())   # dict_values([10, 0])
print("小明" in scores)  # True — 檢查玩家是否存在
集合

Set — 去重與集合運算

# set 自動去除重複
hand = ["A", "7", "A", "K", "7"]
unique = set(hand)
print(unique)       # {'A', '7', 'K'}

# 用途 1:檢查手牌有幾種不同的牌
print(f"有 {len(set(hand))} 種不同的牌")

# 用途 2:找出兩個玩家的共同牌
p1 = {"A", "K", "Q", "7"}
p2 = {"K", "3", "7", "J"}
print(p1 & p2)     # {'K', '7'}  交集 — 共同的牌
print(p1 | p2)     # 聯集 — 所有的牌
print(p1 - p2)     # {'A', 'Q'} 差集 — p1 有但 p2 沒有
1A2B 的 set 用法

之前用 len(set(guess)) != 4 檢查數字是否重複,就是利用 set 去重的特性!

語法

四種資料結構比較

listtupledictset
符號[ ]( ){ k: v }{ }
有順序OOO (3.7+)X
可修改OXOO
可重複OO鍵不行X
取值方式索引 [0]索引 [0]["A"]無法直接取
撲克牌比喻手牌、牌堆(花色, 牌面)牌面→點數有哪些花色
進階

巢狀結構:玩家資訊

list 裡面放 dict,組合出複雜的資料:

players = [
    {"name": "小明", "hand": ["A♠", "K♥", "7♦"], "score": 0},
    {"name": "小華", "hand": ["Q♠", "J♣", "10♥"], "score": 0},
]

# 取得小明的手牌
print(players[0]["hand"])     # ['A♠', 'K♥', '7♦']

# 印出所有玩家的資訊
for p in players:
    print(f"{p['name']}:{p['hand']},{p['score']} 分")

# 找出分數最高的玩家
best = max(players, key=lambda p: p["score"])
print(f"目前領先:{best['name']}")
實作練習

動手做:撲克牌比大小(完整版)

import random

def create_deck():
    suits = ["♠", "♥", "♦", "♣"]
    ranks = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"]
    return [r + s for s in suits for r in ranks]

values = {"A":14,"K":13,"Q":12,"J":11,
          "10":10,"9":9,"8":8,"7":7,"6":6,"5":5,"4":4,"3":3,"2":2}

def get_value(card):
    return values[card[:-1]]   # 去掉花色取牌面

deck = create_deck()
random.shuffle(deck)
p_score, c_score = 0, 0

print("🃏 撲克牌比大小!共 5 回合\n")
for r in range(1, 6):
    input(f"--- 第 {r} 回合,按 Enter 翻牌 ---")
    p, c = deck.pop(), deck.pop()
    pv, cv = get_value(p), get_value(c)
    print(f"你:{p}({pv}點)  vs  電腦:{c}({cv}點)")
    if pv > cv:
        p_score += 1
        print("→ 你贏!\n")
    elif pv < cv:
        c_score += 1
        print("→ 電腦贏!\n")
    else:
        print("→ 平手!\n")

print(f"最終比分 — 你 {p_score} : {c_score} 電腦")
print("🎉 你獲勝!" if p_score > c_score else
      "😢 電腦獲勝!" if c_score > p_score else "🤝 平手!")

第五章完成!

學會了 listtupledictset 四種資料結構,
還用它們組出了一個完整的撲克牌遊戲!