<style>
.alert-info {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
}
</style>

書接上回
先前介紹了Python這門語言的基本安裝和套件管理器
這次就要開始進入Python的語法和基本特性了

# 前言

Python是一門在資料科學和Pwn中常用到的語言
廣受歡迎的理由不外乎是寫起來很方便
雖然是強型別
但簡單的語法和規則讓其受到眾人的喜愛
今天我們就從基本的資料型態和資料結構開始吧

# 觀前提示

  • 本篇文章將主要以CLI方式進行 若有使用上的需求可以參考CLI工作訣竅
  • 本文適用於主流的MacOS、Linux、Windows系統
  • 本文將以Python3.12為案例進行說明 若需求不同請自行調整
  • 請務必知道你自己在做什麼 希望你不要把電腦搞壞
  • 本系列預設你會寫程式 只是要學新語言

# 主文

# 確認Python環境

先前我們安裝過Python了
首先我可以先在終端裡輸入

python3 --version

看看我們的python版本

確認版本無誤後
接著我們可以輸入

python3

此時會看到畫面進入一坨奇怪的輸入
一些版權聲明還有版本資訊
以及一個>>>的符號出現

 python3
Python 3.12.1 (main, Feb 27 2024, 14:28:09) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

進到這裡就跟終端一樣可以輸入python語法作為指令
這裡我們試試看程式人都應該第一個學的最基本的輸出語法

print("Hello, World!")
# Hello, World!

看來一切就緒了
那我們可以準備開始進入編寫一個python檔案了了

# 建立專案

在編寫程式之前
開一個空白的資料夾
存放我們的專案和程式碼
我們也可以使用之前提過的poetry進行專案的初始化和套件管理
不過這裡我們先以基本的純腳本為主

mkdir python_project
cd python_project
touch main.py

迷之音:為什麼不要右鍵創建新檔案呢

接著使用編輯器打開main.py
就可以開始寫程式了

# 基本IO

寫程式最大的目標
正是將使用者的輸入
透過運算和邏輯判別後
輸出給使用者看到回饋

寫Python也不外乎如此

a = input()
print(a)

接著在終端機打上python3 main.py(或者你有裝任何套件可以幫你點一下就執行)
就能夠執行這份檔案了
緊接著隨便輸入點東西按下enter
可以看到系統回吐給你了一份一樣的東西
這樣就有一份基本的IO足以入門了

嗯 你說太快了 不知道在幹嘛?

我們簡單解釋一下我們剛剛做了什麼
首先 input()是一個python自帶的函數
能夠從標準輸出入中讀取字串並且回傳出來
此時 我們將其丟入到一個暫存的a
再使用print()這個函數
a這個變數的內容印出來

或許有點複雜
但這就是一個最基本的IO操作了
我們這裡只先解釋作用
之後再來解釋實際的原理

# Python的變數

前一小節中我們提到
我們將input()的內容存進了一個變數a

# 變數的邏輯

那麼 在python中變數是怎樣的概念呢

可以想像的是
變數就是一個盒子
我們可以將一些想存放的東西放進去
需要時再取出來使用
實際上運作的原理就是程式在記憶體中先預支一個空位
接著將我們的資料存放進去
之後等要用到 再去呼叫該位址進而獲得我們需要的值

# 變數的型別

變數這種資料的容器
想當然 必須有一些規定和限制
不然每個人都把超級大胖子丟進狹小空間還得了

在python中
常見的基本型別有三種
分別是int(整數)float(浮點數)str(字串)
聽名字大概知道
如果我們要存年齡或人數 那我們可以選用int
如果我們要存身高或體重 那我們可以選用float
如果我們要存名字或者一段文字 那我們可以選用str

值得注意的是 在Python中字元也是直接以字串的形式引用的喔

# 變數的型別 之 為什麼要規定

如果有學過其他強型別語言
尤其是C#、Java等類型語言的話
應該會注意到一件事情

在其他語言中
定義變數時的樣子是這樣

int a = 1;
string b = "Hello, World!";

然而 在我們之前的範例中
在宣告變數時並沒有去定義前面的型別
這是因為
Python雖然也是強型別語言
但是它是一門動態語言

因此 當程式底層在執行的時候
會自動將我們的變數進行型別的判斷
所以我們在宣告變數時並不需要特別去指定型別
只需要將

變數名字 =

即可完成變數的宣告

# 變數的命名和型別提示

雖然在Python中 變數的命名並沒有硬性規定
但多數人 起碼筆者我自己使用底線法
也就是_來分隔變數名稱
而不是採用駝峰式的命名(公開class除外)

並且 雖然我們不需要在宣告變數時定義其型態
我們依舊可以可以在變數名字後面
加上型別提示 以便我們之後這個變數是什麼型態避免混亂

a: int = 1
b: str = "Hello, World!"

這樣的命名方式可以讓可讀性更高喔

# python中的註解

有時候 變數在宣告後
如果名字寫得不夠精確
或者寫了也不是很懂他的用途
這時候我們可以使用註解的方式去補充說明

在Python中
註解分為兩種
一種是單行的註解 而另一種是多行的註解
使用方式很簡單

# 這是一個單行註解
"""
這是多行註解
"""

根據自己的狀況使用
有助於幫助自己在後續維護的方便喔

# Python的資料結構

在大學學習程式時
有一門課叫做資料結構
其目的正式要用程式原生的資料格式 利用邏輯進行一定的組合後
實作出一個能夠更有效率的存放資料的容器

然而 在Python中
有許多的資料結構是內建的

# List

這或許是整個Python最外掛的結構了
List是一個能夠存放多個元素的容器
可以存放任何型態的資料
並且同時擁有增刪改查的功能

# 建立一個list
my_list = [1, 2, 3, "hello", True]

# 取值(index從0開始)
print(my_list[0])   # 1
print(my_list[-1])  # True(從尾巴算回來)

# 切片(slicing)
print(my_list[1:3]) # [2, 3]

常用的操作方法

fruits = ["apple", "banana"]

# 新增
fruits.append("cherry")       # 尾巴加一個 -> ["apple", "banana", "cherry"]
fruits.insert(1, "orange")    # 指定位置插入 -> ["apple", "orange", "banana", "cherry"]

# 刪除
fruits.remove("banana")       # 刪除指定元素
fruits.pop()                  # 刪除最後一個並回傳
fruits.pop(0)                 # 刪除指定index並回傳

# 查詢
print(len(fruits))            # 長度
print("apple" in fruits)      # 是否存在(回傳bool)
print(fruits.index("apple"))  # 取得index

# 排序
numbers = [3, 1, 4, 1, 5, 9]
numbers.sort()                # 原地排序
sorted_nums = sorted(numbers) # 回傳新的排序結果(不改變原本的)

List是可以被修改(mutable)的 這點很重要 後面會跟Tuple做比較

# Tuple

Tuple長得跟List很像
差別在於Tuple是不可修改(immutable)

# 建立一個tuple
my_tuple = (1, 2, 3, "hello")

# 取值跟list一樣
print(my_tuple[0])   # 1
print(my_tuple[1:3]) # (2, 3)

# 但你不能改它
my_tuple[0] = 99  # TypeError: 'tuple' object does not support item assignment

你可能會問 那這東西到底有什麼用
既然不能改 為什麼不全部用List就好

答案是安全性和效能
Tuple因為不可修改 所以可以當作dict的key(後面會講)
而且在大量資料的情境下 Tuple的存取效能比List要好
如果你有一組資料確定不會被改動(例如座標、RGB值之類的)
用Tuple比List更合適

# 常見的使用場景
coordinate = (25.0, 121.5)
rgb_color = (255, 128, 0)

# 可以用來做多值回傳
def get_user():
    return ("Miyago", 24)

name, age = get_user()  # 解包(unpacking)

# Dict

Dict全名Dictionary 字典
是一個用key-value配對來存放資料的結構
如果你學過其他語言 它就是Map或HashMap

# 建立一個dict
user = {
    "name": "Miyago",
    "age": 24,
    "job": "SRE"
}

# 取值
print(user["name"])          # Miyago
print(user.get("email"))     # None(用get不會報錯)
print(user.get("email", "N/A"))  # N/A(找不到時的預設值)

# 新增或修改
user["email"] = "miyago@example.com"
user["age"] = 25

# 刪除
del user["email"]
user.pop("job")              # 刪除並回傳值

常用操作

user = {"name": "Miyago", "age": 24, "job": "SRE"}

# 遍歷
for key in user:
    print(key, user[key])

for key, value in user.items():
    print(key, value)

# 取得所有key和value
print(user.keys())    # dict_keys(['name', 'age', 'job'])
print(user.values())  # dict_values(['Miyago', 24, 'SRE'])

# 合併(Python 3.9+)
extra = {"city": "Taipei", "lang": "Python"}
merged = user | extra

Dict的key必須是immutable的型態
所以strinttuple可以當key
list不行 因為list是mutable的
這也是為什麼Tuple有其存在意義的原因之一

# Set

Set是一個無序且不重複的集合
如果你只在乎「有沒有」而不在乎「在哪裡」或「有幾個」
Set就是你最好的朋友

# 建立一個set
my_set = {1, 2, 3, 3, 3}
print(my_set)  # {1, 2, 3} 重複的自動去掉

# 新增和刪除
my_set.add(4)
my_set.remove(1)     # 如果不存在會報錯
my_set.discard(99)   # 如果不存在也不會報錯

# 檢查是否存在
print(3 in my_set)   # True

Set最強大的地方在於集合運算

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print(a & b)   # {3, 4}     交集
print(a | b)   # {1,2,3,4,5,6} 聯集
print(a - b)   # {1, 2}     差集
print(a ^ b)   # {1, 2, 5, 6} 對稱差集

最常見的用途就是去重

names = ["Miyago", "Alice", "Bob", "Miyago", "Alice"]
unique_names = list(set(names))
print(unique_names)  # ['Bob', 'Alice', 'Miyago'](順序不保證)

# 型態之間的比較

最後做一個簡單的整理

結構 有序 可修改 可重複 取值方式
List O O O index
Tuple O X O index
Dict X(3.7+有序) O key不可重複 key
Set X O X 無(只能檢查存在)

嚴格來說 Python 3.7之後Dict是insertion-ordered的
也就是你放進去的順序會被保留
但邏輯上還是不要依賴這個特性比較好

# 結語

以上就是Python中最基本的資料型態和資料結構了
雖然看起來東西不少
但實際上最常用的就是listdict
把這兩個搞熟了基本上就能應付大部分的場景

下一篇預計會講到流程控制(if/for/while)和函數
也就是真正開始寫邏輯的部分了
那我們就下篇見吧

# 參考資料

# 附錄