---
# System prepended metadata

title: xxx
tags: [計算機概論]

---

# 字典 (dictionary)
- 跟串列類似，都是一種儲存資料的容器
- 定義：contains unordered key-value pairs
- 字典內的元素是以key-value對的形式存在，key值是不能重複的。
- 字典的 key 需要是 immutable（不可更改）且 hashable（唯一雜湊值 → 唯一）。
- 常見的 immutable 資料型別包括:
    -   數字(int, float)
    -   字串(str)
    -   元組(tuple)
## 字典的底層原理
### 插入:
當我們初始化一個空字典時，CPython會初始化一個擁有8個None元素的一維Array、一個空的二維Array
```python=
dict1 = {}

'''
indices = [None, None, None, None, None, None, None, None]
entries = []
'''
```
當我們在空字典加入一條數據，python會計算key的hash values，並對這個值取對8的餘數，
0代表這條數據是第一條數據。
```python=
hashValue = hash("小波")
print(hashValue1)
print(hashValue1 % 8)

# 結果:
# -5923010074522229261
# 3
```
```python=
dict1['小波'] = 183

'''
indices = [None, None, None, 0, None, None, None, None]
entries = [[-5923010074522229261, 小波的記憶體位址, 183的記憶體位址]]
'''
```
當我們再加入一條數據時，迪西的hash values對8取餘數是7。
```python=
dict1['小波'] = 183
dict1['迪西'] = 244

'''
indices = [None, None, None, 0, None, None, None, 1]
entries = [ [-5923010074522229261, 小波的記憶體位址, 183的記憶體位址],
             [-588543676937807417, 迪西的記憶體位址, 244的記憶體位址] ]
'''
```
### 查詢
當我們想取得迪希所對應的value，python會根據餘數7找到 indices 中索引7的值是1，並去找entries索引1的值。

所以字典與list相比查詢速度很快，舉個例子:
```python=
list1 = [10, 20, 30, 40, 50]
50 in list1
```
這段程式碼必須走訪整個list才能算出結果

當使用字典:
```python=
dict1 = {10:1, 20:2, 30:3, 40:4, 50:5}
50 in dict1
```
只要將50 hash一下取對5的餘數就能在indices直接取得entries的索引值
資料量越大字典與list的查詢速度的差距就越大。

> 參考:
> <a href='https://blog.csdn.net/weixin_47906106/article/details/121772942'>详解Python字典的底层原理——哈希表(Python面试必备)</a>
> <a href='https://www.cnblogs.com/jiakecong/p/15881370.html'>python字典底层原理</a>
> <a href='https://blog.csdn.net/be5yond/article/details/120021250'>Python 深入理解dict---为什么 dict 查询元素比 list 快？</a>
## 建立一個空字典
```python=
people = {}           # 建立空字典
print("天線寶寶身高辭典", people) # 天線寶寶身高辭典 {}
people['TA'] = 176
people['小波'] = 183
people['迪西'] = 244
people['拉拉'] = 259
people['丁丁'] = 300
print("天線寶寶身高辭典", people)
# 天線寶寶身高辭典 {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300}
```

![upgit_20240813_1723541679.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240813_1723541679.png)

## 字典基本操作
```python
dict1 = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300}
print(dict1["丁丁"]) # 300

# 列出字典資料型態
print("資料型態是: ",type(dict1)) # 資料型態是:  <class 'dict'>
```

## 取值(更新)
```python
person = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300}
# NOTE: key()
print(person.keys())  # dict_keys(['TA', '小波', '迪西', '拉拉', '丁丁'])
# 檢查 key 是否存在
print('TA' in person)  # 輸出: True
print('助教' in person)  # 輸出: False


# NOTE: value()
print(person.values()) # dict_values([176, 183, 244, 259, 300])
print(person['TA'])  
print(person.get('TA'))  
person['TA'] = 180 # 設置 value


# NOTE: item (key-value 對)
print(person.items()) # dict_items([('TA', 180), ('小波', 183), ('迪西', 244), ('拉拉', 259), ('丁丁', 300)])

# 遍歷 item
for key, value in person.items():
    print(f"Key: {key}, Value: {value}")
```

## 新增字典特定元素
```python
person = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300}
person["利姆路·坦派斯特"]=130 # 更新跟新增一樣

print(person)
# {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, '利姆路·坦派斯特': 130}
```

<!-- ![image](https://hackmd.io/_uploads/rJIp4AjO0.png) -->
<!-- ![image](https://hackmd.io/_uploads/B1WNoCodA.png) -->


## 刪除字典元素

### 刪除字典特定元素
```python
person = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, "利姆路·坦派斯特": 130}
del person['小波']
print("新字典內容:", person)
# 新字典內容: {'TA': 176, '迪西': 244, '拉拉': 259, '丁丁': 300, '利姆路·坦派斯特': 130}
```

### 刪除字典所有元素
```python
person = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, "利姆路·坦派斯特": 130}
person.clear( )
print("新字典內容:", person) # 新字典內容: {}
```


```python
person = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, "利姆路·坦派斯特": 130}
copy = person.copy( )
print("位址 = ", id(person), "  person元素 = ", person)
print("位址 = ", id(copy), "  copy元素 = ", copy)

# 位址 =  1802586334400   person元素 =  {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, '利姆路·坦派斯特': 130}
# 位址 =  1802586329216   copy元素 =  {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, '利姆路·坦派斯特': 130}
```

## 取得字典元素數量
```python
person = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, "利姆路·坦派斯特": 130}

print("person字典元素數量= ", len(person)) # person字典元素數量=  6
```

## 檢驗是否存在於字典
```python
person = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, "利姆路·坦派斯特": 130}
key = input("請輸入鍵(key) = ")
print("鍵(key) = ", key)
value = input("請輸入值(value) = ")
print("值(value) = ", value)
if key in person:
    print(f"{key}已經在字典了")
else:
    person[key] = value
    print("新的person字典內容 = ", person)

# 鍵(key) =  蜜莉姆·拿渥
# 值(value) =  135
# 新的person字典內容 =  {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, '利姆路·坦派斯特': 130, '蜜莉姆·拿渥': '135'}
```


## 字典合併
```python
personA = {'TA': 176, '小波': 183}
personB = {'利姆路·坦派斯特': 130, '蜜莉姆·拿渥': 135}
personA.update(personB) # A的後面加B
print(personA)
# {'TA': 176, '小波': 183, '利姆路·坦派斯特': 130, '蜜莉姆·拿渥': 135}
```

## 搭配字典使用的函數
### len()
```python
people = {
      "維爾德拉·坦派斯特":{
            "身高":130,
            "瞳色": "金瞳",
            "萌點": {"傲嬌","中二病"}
      }, 
      "蜜莉姆·拿渥":{
            "別號": "破壞之暴君噠",
            "身高":135,
            "瞳色": "藍瞳",
            "萌點": {"裝","條紋過膝襪","噠"}
      }
}

# 列印字典元素個數
print("people字典元素個數", len(people))
# people字典元素個數 2

print("people字典元素個數['維爾德拉·坦派斯特']元素個數 ", len(people['維爾德拉·坦派斯特']))
# people字典元素個數['維爾德拉·坦派斯特']元素個數  3

print("people字典元素個數['蜜莉姆·拿渥']元素個數  ", len(people['蜜莉姆·拿渥']))
# people字典元素個數['蜜莉姆·拿渥']元素個數   4
```


### get() 如果沒有找到指定的值，就返回預設值
```python
person = {'TA': 176, '小波': 183, '迪西': 244, '拉拉': 259, '丁丁': 300, "利姆路·坦派斯特": 130}
temp = person.get("蜜莉姆·拿渥")
print(temp) # None

temp = person.get("蜜莉姆·拿渥", 135)
print(temp) # 135
```

### setdefault()
- 這個方法基本上與 get（）相同，不同之處在於 get（）方法不會改變字典內容。使用setdefault（）方法時若所搜尋的鍵不在，會將"鍵：值"加入字典，如果有設定預設值則將鍵：預設值加入字典，如果沒有設定預設值則將鍵：none 加入字典。
```python=
# key在字典內
person = {'TA': 176,  "利姆路·坦派斯特": 130}
temp = person.setdefault("蜜莉姆·拿渥")
print("temp = ", temp) # temp =  None
print("person字典", person)
# person字典 {'TA': 176, '利姆路·坦派斯特': 130, '蜜莉姆·拿渥': None}

del person['蜜莉姆·拿渥']

temp = person.setdefault("蜜莉姆·拿渥", 135)
print("temp = ", temp) # temp =  135
print("person字典", person)
# person字典 {'TA': 176, '利姆路·坦派斯特': 130, '蜜莉姆·拿渥': 135}
```

# 集合(set)
- 一個集合裡所有的鍵都不會重複，元素裡的內容可以是整數(int)、浮點數(float)、字串(string)、元組(tuple)、布林(boolen)，但是不可以是串列(list)、字典(dict)、集合(set)。
- 因為集合不會包含重複的資料的特性，常用來進行去除重複的字元、或判斷元素間是否有交集、聯集或差集之類的關聯性。

## 宣告
```python
# 方式1
fruits = ['apple', 'orange', 'apple', 'banana', 'orange']
x = set(fruits) # {'orange', 'apple', 'banana'}
print(x)
# 方式2
y = set(['apple', 'orange', 'apple', 'banana', 'orange'])
print(y) # {'orange', 'apple', 'banana'}
```

## add() 添加元素到集合中
```python
my_set = {1, 2, 3}
my_set.add(4)
print(my_set) # {1, 2, 3, 4}
```

## clear() 清空集合中的所有元素。
```python
my_set = {1, 2, 3}
my_set.clear()
print(my_set) # set()
```

## copy()  複製集合。
```python
original_set = {1, 2, 3}
copied_set = original_set.copy()
print(original_set) # {1, 2, 3}
print(copied_set) # {1, 2, 3}
```

## discard() 移除集合中的指定元素，如果元素不存在，则不发生任何操作。
```python
my_set = {1, 2, 3, 4}
my_set.discard(3)
print(my_set) # {1, 2, 4}
```

## 集合差集 difference()
```python
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
result = set1.difference(set2) # 输出: {1, 2}
```

## 集合交集 intersection()
```python
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
result = set1.intersection(set2) # 输出: {3, 4}
```

## 判断是否沒有交集 isdisjoint()
```python
set1 = {1, 2, 3}
set2 = {4, 5, 6}
result = set1.isdisjoint(set2) # True，因为两个集合没有交集
```

## issubset() 判断一个集合是否是另一个集合的子集 
```python
set1 = {1, 2}
set2 = {1, 2, 3, 4}
result = set1.issubset(set2) # True，因为set1是set2的子集
```

## issuperset() 判断一个集合是否是另一个集合的超集 
```python
set1 = {1, 2, 3, 4}
set2 = {1, 2}
result = set1.issuperset(set2) # True，因为set1是set2的超集
```

## union()
```python
set1 = {1, 2, 3}
set2 = {3, 4, 5}
result = set1.union(set2) # 输出: {1, 2, 3, 4, 5}
```
## set comprehension
如 list comprehension一樣，
set也有set comprehension
```python=
a = {x for x in 'abracadabra' if x not in 'abc'}
# >>>a: {r, d}
```
> 參考:
> [5. 資料結構— Python 3.12.5 說明文件](https://docs.python.org/zh-tw/3/tutorial/datastructures.html)

## lab作業題目
- 繳交方式 : 將程式碼以及程式輸出截圖放入名為'學號_Lab05'的資料夾，並上傳到github
- 繳交時間 : 9/25 12:00

### lab05_01
- 有一天助教需要紀錄每個學生的多次考試成績，並計算每個學生的平均成績。請你幫助教設計一個程式，輸入每個學生的多次考試成績，然後計算每個學生的平均成績。
- 學生的成績如下：
```python=
scores = {
    'Alice': [85, 87, 90],
    'Bob': [78, 82, 89],
    'Charlie': [95, 92, 88],
    'David': [64, 68, 70],
    'Eve': [90, 91, 85]
}
```
- 範例程式輸出如下：
![upgit_20240813_1723541807.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240813_1723541807.png)
- 解答：
```python=
scores = {
    'Alice': [85, 87, 90],
    'Bob': [78, 82, 89],
    'Charlie': [95, 92, 88],
    'David': [64, 68, 70],
    'Eve': [90, 91, 85]
}

average_scores = {student: sum(grades) / len(grades) for student, grades in scores.items()}

for student, avg in average_scores.items():
    print(f"{student}: {avg:.2f}")

```

### lab05_02
- 有一天助教需要紀錄班上每個學生的成績，並找到成績最高的學生。請你幫助教設計一個程式，輸入每個學生的成績，然後找到成績最高的學生。
- 學生的成績如下：
```python=
scores = {
    'Alice': 85,
    'Bob': 92,
    'Charlie': 78,
    'David': 64,
    'Eve': 90
}
```
- 範例程式輸出如下：
![upgit_20240813_1723541826.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240813_1723541826.png)
- 解答：
```python=
scores = {
    'Alice': 85,
    'Bob': 92,
    'Charlie': 78,
    'David': 64,
    'Eve': 90
}

highest_score = max(scores.values())
top_student = [student for student, score in scores.items() if score == highest_score]

print(f"成績最高的學生是 {top_student[0]}，分數是 {highest_score}")

```

### lab05_03
- 有一天助教需要紀錄班上每個學生的詳細資料，並找到年齡最大的學生。請你幫助教設計一個程式，輸入每個學生的詳細資料(包括年齡)，然後找到年齡最大的學生。
- 學生的資料如下：
```python=
students = {
    'Alice': {'age': 20, 'grade': 'A'},
    'Bob': {'age': 22, 'grade': 'B'},
    'Charlie': {'age': 19, 'grade': 'A'},
    'David': {'age': 21, 'grade': 'C'},
    'Eve': {'age': 20, 'grade': 'B'}
}

```
- 範例程式輸出如下：
![upgit_20240813_1723541850.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240813_1723541850.png)

- 解答：
```python=
students = {
    'Alice': {'age': 20, 'grade': 'A'},
    'Bob': {'age': 22, 'grade': 'B'},
    'Charlie': {'age': 19, 'grade': 'A'},
    'David': {'age': 21, 'grade': 'C'},
    'Eve': {'age': 20, 'grade': 'B'}
}

oldest_age = max(student['age'] for student in students.values())
oldest_student = [name for name, info in students.items() if info['age'] == oldest_age]

print(f"年齡最大的學生是 {oldest_student[0]}，年齡是 {oldest_age}")
```
### lab05_04
- 有一天助教需要統計班上學生選修課程的聯集。請你幫助教設計一個程式，輸入每個學生選修的課程，然後找到所有學生選修過的所有課程。
- 學生選修的課程如下：
```python=
students = {
    'Alice': {'Math', 'Science'},
    'Bob': {'Math', 'History', 'Science'},
    'Charlie': {'Math'},
    'David': {'Science', 'History'},
    'Eve': {'Math', 'Science', 'History'}
}
```
- 範例程式輸出如下：
![upgit_20240813_1723541878.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240813_1723541878.png)
- 解答：
```python=
students = {
    'Alice': {'Math', 'Science'},
    'Bob': {'Math', 'History', 'Science'},
    'Charlie': {'Math'},
    'David': {'Science', 'History'},
    'Eve': {'Math', 'Science', 'History'}
}

all_courses = set.union(*students.values())

print(f"所有學生選修過的課程有：{', '.join(all_courses)}")
```


### lab05_05
- 有一天助教需要統計班上某兩個學生選修課程的差集。請你幫助教設計一個程式，輸入這兩個學生選修的課程，然後找到其中一個學生選修但另一個學生沒選修的課程。
- 請幫助教找到 student_A 選修但 student_B 沒選修的課程並輸出結果：
- 學生選修的課程如下：
```python=
student_A = {'Math', 'Science', 'Art'}
student_B = {'Math', 'History'}
```
- 範例程式輸出如下：
![upgit_20240813_1723541896.png](https://raw.githubusercontent.com/kcwc1029/obsidian-upgit-image/main/2024/08/upgit_20240813_1723541896.png)
- 解答：
```python=
student_A = {'Math', 'Science', 'Art'}
student_B = {'Math', 'History'}

unique_courses = student_A.difference(student_B)

print(f"student_A 選修但 student_B 沒選修的課程有：{', '.join(unique_courses)}")
```



