ข้ามไปยังเนื้อหา

Mental Model — คิดแบบ Python

ใน TypeScript compiler (tsc) คือด่านป้องกันด่านแรก จับ type ที่ไม่ตรงกันตั้งแต่ก่อนโค้ดได้รัน พอย้ายมา Python ด่านนั้นหายไป เหลือแค่ runtime — interpreter ไม่ตรวจ type ให้เลย

Python มี type hints ให้ใช้ (และคุณควรใช้) แต่เป็นแค่ hint ถ้าอยากให้บังคับจริงต้องพึ่ง tool แยกอย่าง mypy หรือ pyright

Mental model: TypeScript บอกว่า “พิสูจน์ก่อนว่าปลอดภัย แล้วค่อยรัน” ส่วน Python บอกว่า “รันเลย ถ้าส่ง type ผิดค่อยเจอ runtime error เอา”

ตัวแปรใน Python เป็นแค่ label ที่ชี้ไปยัง object ตัวไหนก็ได้ และ type ติดอยู่กับ object ไม่ได้ติดอยู่กับชื่อตัวแปร ต่างจาก TypeScript ที่ตัวแปรถือ type ของตัวเองมาตั้งแต่ตอน compile

TypeScript
// TypeScript: variable has a compile-time type
let count: number = 0;
count = "five"; // Error: Type 'string' is not assignable to type 'number'
function double(n: number): number {
return n * 2;
}
Python
# Python: variable is just a label
count = 0
count = "five" # perfectly legal at runtime
def double(n: int) -> int: # hints are documentation only
return n * 2
print(double(3)) # 6
print(double("x")) # TypeError at RUNTIME, not before

TypeScript ต้องคอมไพล์เป็น JavaScript ก่อนถึงจะรันได้ ส่วน Python รันผ่าน interpreter — สั่ง python3 ใส่ source file ตรง ๆ ได้เลย ตอน develop ไม่มีขั้นตอน build

TypeScript
# TypeScript workflow
# tsc index.ts → compiles to index.js
# node index.js → runs the compiled output
# ts-node index.ts → shortcut for development
Python
# Python workflow
# python3 script.py → runs directly, no compile step
# python3 → opens the interactive REPL
# python3 -c "print(1)" → run a one-liner inline

ข้อนี้คือสิ่งที่ TypeScript developer สังเกตเห็นเป็นอย่างแรก เพราะ Python ใช้การย่อหน้า (4 spaces ตามข้อกำหนด) กำหนดขอบเขตของ block ไม่มี { } ให้ยึด และ colon (:) คือสิ่งที่เปิด block ใหม่เสมอ

TypeScript
// TypeScript
function classify(n: number): string {
if (n > 0) {
return "positive";
} else if (n < 0) {
return "negative";
} else {
return "zero";
}
}
Python
# Python — indentation IS the block
def classify(n: int) -> str:
if n > 0:
return "positive"
elif n < 0: # note: elif, not else if
return "negative"
else:
return "zero"
print(classify(5)) # positive
print(classify(-3)) # negative
print(classify(0)) # zero

ย่อหน้าผิดเมื่อไหร่ก็เป็น syntax error ทันที โชคดีที่ editor สาย Python ส่วนใหญ่จัด indentation 4 spaces ให้อัตโนมัติอยู่แล้ว

TypeScript ยืดหยุ่นมาก จะใช้ class, functional composition, prototype chain หรือ plain object สลับกันไปมาก็ได้ ส่วน Python เชียร์ให้เหลือทางเดียวที่เป็นธรรมชาติที่สุด และชุมชนเรียกสไตล์นั้นว่า Pythonic — โค้ดที่กระชับ อ่านง่าย และใช้ฟีเจอร์ของภาษาตรงตามที่ออกแบบมา

TypeScript
// TypeScript: multiple ways to filter
const evens1 = nums.filter(n => n % 2 === 0);
const evens2 = nums.reduce((acc, n) => n % 2 === 0 ? [...acc, n] : acc, []);
// Both work; TS community accepts both
Python
# Python: list comprehension is the Pythonic way
nums = [1, 2, 3, 4, 5, 6]
evens = [n for n in nums if n % 2 == 0]
print(evens) # [2, 4, 6]
# Works but less Pythonic:
# evens = list(filter(lambda n: n % 2 == 0, nums))

method ของ class ใน TypeScript ได้ this มาโดยปริยาย ส่วนใน Python ต้องประกาศ parameter self เป็น argument ตัวแรกให้ชัดเจน — interpreter ใส่ค่าให้เองตอนคุณเรียก method แต่คนที่ต้องเขียนประกาศไว้คือคุณ

TypeScript
// TypeScript: implicit this
class Counter {
private count: number = 0;
increment(): void {
this.count++; // 'this' is implicit
}
value(): number {
return this.count;
}
}
Python
# Python: explicit self
class Counter:
def __init__(self): # called on Counter()
self.count = 0 # instance attribute
def increment(self): # self is the instance
self.count += 1
def value(self) -> int:
return self.count
c = Counter()
c.increment()
c.increment()
print(c.value()) # 2

type ของ TypeScript ถูกลบทิ้ง — tsc เอาออกหมด JavaScript ที่รันจริงไม่รู้ด้วยซ้ำว่าเคยมี ส่วน Python เก็บ annotation ไว้เป็น object จริง ซึ่งเมื่อก่อนแปลว่ามันถูก evaluate ทันทีตอน import ทำให้ type ที่ยังไม่มีตัวตนต้องเขียนครอบด้วย string

Python 3.14 (PEP 649) เปลี่ยนเรื่องนี้: annotation ถูกเก็บเป็นฟังก์ชันที่ evaluate แบบ lazy และจะคำนวณก็ต่อเมื่อมีคนขอใช้จริง

TypeScript
// TypeScript — types are erased before runtime
interface Node { parent: Node | null; } // self-reference is free
function walk(n: Node): void { /* ... */ }
// Compiled JS has no trace of Node at all
Python
# Python 3.14 — annotations are evaluated on demand
class Node:
parent: Node | None # no quotes needed — not evaluated yet
def add(self, child: Node) -> Node: # forward reference just works
return child
# Read them when you actually want them:
from annotationlib import get_annotations
print(get_annotations(Node.add))

ผลที่ได้จริงมีสองข้อ: เลิกใส่ quote ครอบ forward reference ได้ และไม่ต้องพิมพ์ from __future__ import annotations ไว้หัวไฟล์ทุกไฟล์อีกต่อไป ส่วนไลบรารีที่อ่าน annotation ตอน runtime — Pydantic, FastAPI, dataclasses — จะอ่านผ่าน annotationlib แทนการแตะ __annotations__ ตรง ๆ

# Mental model: types live on objects, not variables
x = 42
print(type(x).__name__) # int
x = "hello"
print(type(x).__name__) # str
# Significant indentation
def classify(n: int) -> str:
if n > 0:
return "positive"
elif n < 0:
return "negative"
else:
return "zero"
for num in [5, -3, 0]:
print(f"{num} is {classify(num)}")
# Pythonic list comprehension
evens = [n for n in range(1, 11) if n % 2 == 0]
print("Evens:", evens)
# Explicit self
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
def value(self) -> int:
return self.count
c = Counter()
c.increment()
c.increment()
print("Counter:", c.value())
ใน Python type ของค่าถูกเก็บไว้ที่ไหน?
อะไรเปิด indented block ใหม่ใน Python?
ทำไม Python class methods ต้องประกาศ `self` เป็น parameter แรก?
วิธีแบบ Pythonic ในการกรองเลขคู่จาก list คืออะไร?