Home
探索 Uedu
學生控制台
註冊會員/登入
研究知情同意中心
教師控制台
課程設定
支援與訊息
Uptime 數據

UeduGPTs

--

Jupyters

4

UG26 CISOSE26
臺北 AQI 46 · 臺中 AQI 28 · 臺南 AQI 24 · 高雄 AQI 33

AI 回覆桌面通知

AI 助教回覆完成時顯示桌面通知

聊天訊息通知

同學在討論區發送訊息時通知

聲音通知

每當有新通知時播放提示音

變數、型別與運算

Python 變數、型別與運算:從一張收據開始

用一支合購分帳小程式,帶你掌握變數賦值、動態型別、四大基本型別、算術與邏輯運算子、型別轉換與整數除法,最後深入 Python 萬物皆物件的記憶體模型。

從一張收據開始:讓程式記住每一筆數字

想像你正在寫一支小程式,幫同學算這學期合購教科書的分帳。三個人合買一本 720 元的書、運費 60 元,要算出每人該付多少、找零幾元。你大可以把 720、60、3 這些數字直接散落在算式裡,但只要書價一改,你就得在整段程式裡到處翻找、逐一修改——這正是「變數」要解決的問題。

變數讓程式可以「記住」一個值,並用一個有意義的名字稱呼它。改一處,全程式跟著變。這一篇,我們就從變數出發,一路走到型別、運算子與型別轉換,把 Python 處理資料的基本骨架建立起來。建議你打開一個 Python 直譯器(在終端機輸入 pythonpython3),每讀到一段程式就親手敲一次,感受會完全不同。

變數、型別與運算概念示意圖

變數與賦值:名字綁到值

在 Python 裡,建立變數就是用等號 = 把一個值「賦」給一個名字:

book_price = 720     # 書價
shipping = 60        # 運費
people = 3           # 分攤人數

total = book_price + shipping
per_person = total // people   # 每人付多少(整數除法,稍後會解釋)

print(total)        # 輸出:780
print(per_person)   # 輸出:260

這裡有幾個值得注意的地方。等號 = 不是數學上的「相等」,而是「賦值」這個動作:把右邊算出的值,綁到左邊的名字上。所以 total = book_price + shipping 是先算出右邊的 780,再把它綁給 total

正因為如此,下面這種寫法在數學上荒謬,在程式裡卻完全合理:

count = 10
count = count + 1   # 先取出舊的 count(10)加 1,再綁回 count
print(count)        # 輸出:11

變數命名請遵循 Python 的慣例(PEP 8 精神):使用小寫加底線的 snake_case,名字要能說明用途。per_personppx 清楚得多,未來的你會感謝現在的你。

# 推薦:見名知意
student_count = 42

# 反模式:意義不明,請避免
sc = 42
x = 42

動態型別:值有型別,名字沒有

C、Java 這類語言要求你先宣告變數的型別(int x = 5;),之後 x 就只能裝整數。Python 不是這樣——它是動態型別(dynamically typed):型別屬於「值」,不屬於「名字」。同一個名字,前一刻綁整數、下一刻綁字串,完全合法:

data = 100      # 此刻 data 綁的是整數
data = "hello"  # 現在 data 改綁字串,沒問題
data = 3.14     # 再改綁浮點數,也沒問題

這帶來彈性,但也要求你心裡清楚每個變數「現在」裝的是什麼。想知道某個值的型別,用內建函式 type()

print(type(100))       # 輸出:<class 'int'>
print(type(3.14))      # 輸出:<class 'float'>
print(type("hello"))   # 輸出:<class 'str'>
print(type(True))      # 輸出:<class 'bool'>

四種最基本的型別

剛起步時,你會反覆遇到四種內建型別:整數、浮點數、字串、布林。

整數 int:沒有小數點的數,Python 的整數可以無限大(只受記憶體限制),不像許多語言有 32 位元上限:

big = 2 ** 100   # 2 的 100 次方
print(big)       # 輸出:1267650600228229401496703205376
print(type(big)) # 輸出:<class 'int'>

浮點數 float:帶小數點的數。只要式子裡出現小數,結果通常就是浮點數:

height = 1.72
print(type(height))     # 輸出:<class 'float'>
print(type(10 / 3))     # 輸出:<class 'float'>(除法 / 永遠回傳 float)

字串 str:用單引號或雙引號包起來的文字。兩者效果相同,挑一種風格保持一致即可:

name = "陳同學"
greeting = '你好'
full = greeting + "," + name   # 用 + 串接字串
print(full)                     # 輸出:你好,陳同學

布林 bool:只有 TrueFalse 兩個值(注意首字母大寫),用來表達「成立與否」。它其實是整數的特例,True 等於 1、False 等於 0:

is_passed = True
print(is_passed + 1)   # 輸出:2(True 當作 1 參與運算)

運算子:算術、比較、邏輯

算術運算子做數學計算:

print(7 + 3)    # 輸出:10  加
print(7 - 3)    # 輸出:4   減
print(7 * 3)    # 輸出:21  乘
print(7 / 3)    # 輸出:2.3333333333333335  除(結果是 float)
print(7 ** 2)   # 輸出:49  次方

比較運算子問一個是非題,結果一定是布林值:

print(5 > 3)    # 輸出:True
print(5 == 5)   # 輸出:True   注意:判斷相等用 ==,不是 =
print(5 != 3)   # 輸出:True   != 是「不等於」
print(5 <= 4)   # 輸出:False

請特別記住:= 是賦值,== 才是比較相等。把兩者搞混是初學者最常見的錯誤之一。

邏輯運算子 andornot 把布林值組合起來:

age = 20
has_id = True

print(age >= 18 and has_id)   # 輸出:True   兩者都成立才 True
print(age < 18 or has_id)     # 輸出:True   只要一個成立就 True
print(not has_id)             # 輸出:False  反轉真假

當多種運算子混用時,優先順序是:算術 → 比較 → 邏輯。不確定時,用括號把意圖寫清楚,既不會算錯,也讓讀者一眼看懂:

score = 85
result = (score >= 60) and (score <= 100)
print(result)   # 輸出:True

型別轉換:在型別之間搬運

不同型別有時需要互相轉換。Python 提供同名的轉換函式 int()float()str()bool()

最常見的場景是處理使用者輸入。input() 拿到的永遠是字串,要拿來計算就得先轉成數字:

text = "42"
number = int(text)       # 字串轉整數
print(number + 8)        # 輸出:50

price = float("3.99")    # 字串轉浮點數
print(price)             # 輸出:3.99

count = 7
message = "共 " + str(count) + " 人"   # 數字轉字串才能串接
print(message)                         # 輸出:共 7 人

轉換不是萬能的。把一個不像數字的字串硬轉成 int 會直接報錯:

int("abc")   # ValueError: invalid literal for int() with base 10: 'abc'

int() 把浮點數轉整數時是直接捨去小數(往零的方向截斷),不是四捨五入,這點容易踩雷:

print(int(3.9))    # 輸出:3   小數被捨去,不是 4
print(int(-2.7))   # 輸出:-2  往零截斷

bool() 的轉換規則也值得認識:00.0、空字串 ""None 都會轉成 False,其餘多半是 True

print(bool(0))     # 輸出:False
print(bool(""))    # 輸出:False
print(bool(42))    # 輸出:True
print(bool("a"))   # 輸出:True

None:明確表示「沒有值」

有時你需要表達「這裡目前還沒有值」,而不是 0 或空字串。Python 為此提供一個特殊物件 None,它自成一個型別 NoneType

answer = None
print(answer)         # 輸出:None
print(type(answer))   # 輸出:<class 'NoneType'>

None 常用來當變數的初始狀態,或表示「函式沒有回傳任何東西」。判斷一個變數是不是 None,慣例是用 is,而不是 ==

selected = None

# 推薦寫法
if selected is None:
    print("尚未選擇")   # 輸出:尚未選擇

# 反模式:能動但不符慣例,請避免
# if selected == None:

整數除法與取餘:分得乾乾淨淨

回到開頭的分帳問題。一般除法 / 會給你帶小數的結果,但「三個人分十個雞蛋」這種問題,你要的是「每人幾個、剩幾個」,這就要用整數除法 // 和取餘 %

eggs = 10
people = 3

print(eggs // people)   # 輸出:3   每人 3 個(整數除法,捨去小數)
print(eggs % people)    # 輸出:1   剩下 1 個(餘數)

這兩個運算子用途極廣。判斷奇偶、做進位、把秒數拆成分與秒,都靠它們:

seconds = 200
print(seconds // 60, "分", seconds % 60, "秒")   # 輸出:3 分 20 秒

n = 17
print(n % 2 == 0)   # 輸出:False(餘數不為 0,所以是奇數)

動手寫一段:合購分帳機

把這一篇學到的東西組合起來,寫一支完整的分帳小程式:

# 合購分帳機
book_price = 720      # 書價
shipping = 60         # 運費
people = 3            # 分攤人數

total = book_price + shipping        # 總金額
per_person = total // people         # 每人應付(整數元)
remainder = total % people           # 無法整除的零頭

# 組出可讀的報告(數字要轉成字串才能串接)
report = (
    "總金額:" + str(total) + " 元\n"
    + "每人應付:" + str(per_person) + " 元\n"
    + "零頭:" + str(remainder) + " 元(由發起人吸收)"
)
print(report)

# 預期輸出:
# 總金額:780 元
# 每人應付:260 元
# 零頭:0 元(由發起人吸收)

試著把 book_price 改成 700,再跑一次,看看 per_personremainder 怎麼變化。你會發現:因為用變數記住了書價,整支程式只要改那一行,其餘自動跟著更新——這正是開頭那張收據想說明的價值。

重點回顧:初學者最常踩的雷

  • === 不一樣= 是賦值(把值綁給名字),== 是比較是否相等。在 if 條件裡誤用 = 是經典錯誤。
  • / 永遠回傳浮點數。即使 10 / 2 也會得到 5.0 而非 5。需要整數結果就用 //
  • int() 是截斷不是四捨五入int(3.9) 得到 3。要四捨五入請改用 round()
  • 數字與字串不能直接相加"共 " + 7 會報 TypeError,必須先 str(7) 轉成字串,或改用 f-string。
  • 判斷 Noneis None,而不是 == None,這是 Python 社群的明確慣例。

深入探討(研究所視角):萬物皆物件

前面我們把變數說成「名字綁到值」,現在把這句話講透。在 Python 裡,萬物皆物件(everything is an object)——整數、字串、布林、函式、甚至型別本身,全都是物件。每個物件在記憶體中有三個固有屬性:身分(identity)、型別(type)、值(value)。身分可以用內建函式 id() 取得,它在 CPython 中對應到該物件的記憶體位址:

a = 256
print(id(a), type(a))   # 例如:140703... <class 'int'>

賦值 a = 256 真正發生的事,是建立(或取得)一個值為 256 的整數物件,再讓名字 a 指向它。所以變數不是「裝值的盒子」,而比較像是「貼在物件上的標籤」。多個名字可以貼在同一個物件上:

x = [1, 2, 3]
y = x            # y 與 x 指向「同一個」串列物件
print(x is y)    # 輸出:True(is 比較的是身分,不是值)
print(id(x) == id(y))   # 輸出:True

這就帶出 Python 最重要、也最容易出錯的分野:可變(mutable)與不可變(immutable)

intfloatstrbooltupleNone 都是不可變的。你無法改變一個整數物件本身;所謂「修改」其實是讓名字改指到另一個新物件:

n = 10
print(id(n))     # 假設是位址 A
n = n + 1
print(id(n))     # 換成位址 B——n 指向了一個全新的物件 11,原本的 10 沒被改動

listdictset可變的。你可以原地修改物件內容,身分(id)不變:

nums = [1, 2, 3]
print(id(nums))     # 假設是位址 C
nums.append(4)
print(nums)         # 輸出:[1, 2, 3, 4]
print(id(nums))     # 仍是位址 C——同一個物件,只是內容變了

可變性的陷阱在於共享參考(shared reference)。承接前面的例子,因為 y = x 讓兩個名字指向同一個串列,透過任一個名字修改,另一個也會「看到」變化:

x = [1, 2, 3]
y = x
y.append(99)
print(x)   # 輸出:[1, 2, 3, 99]——明明只動了 y,x 也變了

若你要的是一份獨立副本,必須明確複製,例如 y = x[:]y = x.copy()。對不可變物件就沒有這個煩惱,因為它根本無法被原地修改。

最後一個常讓人困惑的細節:CPython 會快取小整數(通常是 −5 到 256)與部分短字串,讓相同的值共用同一個物件以節省記憶體。這導致 is 在小數字上「看似」可用:

a = 256
b = 256
print(a is b)   # 輸出:True(256 在快取範圍內,共用物件)

c = 257
d = 257
print(c is d)   # 輸出:可能 False(超出快取範圍,是兩個不同物件)

這正是為什麼比較「值是否相等」要用 ==,比較「是否為同一物件」才用 is——is None 之所以正確,恰恰因為整個程式裡 None 永遠是獨一無二的同一個物件。理解了物件模型,你對賦值、參考與可變性的直覺就會從「會用」升級為「知其所以然」,這在日後處理函式參數傳遞、預設可變參數等進階陷阱時,會是你最可靠的判斷依據。

AI 共讀助教正在陪你讀:Python 變數、型別與運算:從一張收據開始
嗨!我是這篇文章的共讀助教,只根據〈Python 變數、型別與運算:從一張收據開始〉的內容回答。可以問我「解釋某段」「舉個例子」「出題考我」,或反白文中段落後點下方「解釋選取段落」。