พื้นฐานและไวยากรณ์
ปรัชญาการออกแบบของ Python
หัวข้อที่มีชื่อว่า “ปรัชญาการออกแบบของ Python”Python ถูกออกแบบมาบนหลักการเดียว: โค้ดควรอ่านได้เหมือนภาษาอังกฤษธรรมดา Guido van Rossum ผู้สร้าง Python เลือกสละความยืดหยุ่นบางส่วนเพื่อแลกกับความอ่านง่าย
ปรัชญานี้ถูกบันทึกไว้ใน PEP 20 หรือที่รู้จักกันว่า The Zen of Python
รัน import this ในเซสชัน Python ใดก็ได้เพื่อดู
สามบรรทัดที่กำหนดทิศทางทุกอย่าง:
- Beautiful is better than ugly. — สวยงามดีกว่าน่าเกลียด
- Explicit is better than implicit. — ชัดเจนดีกว่าซ่อนเร้น
- There should be one — and preferably only one — obvious way to do it. — ควรมีวิธีเดียวที่ชัดเจน
บรรทัดสุดท้ายสำคัญที่สุดสำหรับการเรียนรู้: เมื่อคุณค้นหาวิธีทำสิ่งใดสิ่งหนึ่งใน Python มักจะมีคำตอบที่ได้รับการยอมรับอยู่เสมอ
การเยื้องบรรทัดที่มีความหมาย
หัวข้อที่มีชื่อว่า “การเยื้องบรรทัดที่มีความหมาย”Python ใช้ การเยื้อง (4 ช่องว่างต่อระดับ ตามมาตรฐาน) เพื่อกำหนดบล็อกโค้ด ไม่มีวงเล็บปีกกาและไม่มีเครื่องหมายเซมิโคลอน
def is_even(n: int) -> bool: if n % 2 == 0: return True return False
for i in range(5): if is_even(i): print(i, "is even")การผสม tab กับ space จะทำให้เกิด TabError
มาตรฐานของชุมชน (PEP 8) คือ 4 ช่องว่าง — ตั้งค่า editor ครั้งเดียวแล้วลืมได้เลย
Thesis ที่ว่า “ทุกอย่างคือ object”
หัวข้อที่มีชื่อว่า “Thesis ที่ว่า “ทุกอย่างคือ object””นี่คือแนวคิดเดียวที่บทเรียนที่เหลือในคอร์สนี้จะย้อนกลับมาซ้ำ ๆ: ใน Python ทุกอย่างที่คุณตั้งชื่อได้คือ object — ไม่ใช่แค่สิ่งที่ดูเหมือนข้อมูล ตัวเลข, function, class, module, และแม้แต่ type เองล้วนมี identity (id()), type (type()), และชุด attribute:
print(type(42)) # <class 'int'>print((42).bit_length()) # 6 -- an int has methods, because it's an objectprint(type(print)) # <class 'builtin_function_or_method'>print(type(int)) # <class 'type'> -- int itself is an instance of typeprint(type(type)) # <class 'type'> -- type is an instance of itselfint ไม่ใช่ primitive keyword พิเศษ — มันคือ object ของ type type (class หนึ่ง) เหมือนกับที่ Dog จะเป็นถ้าคุณนิยามมันเอง print ก็ไม่ใช่ syntax พิเศษเหมือนกัน — มันคือ object ที่เรียกได้ (callable) ดึงมาเก็บได้, กำหนดให้ชื่ออื่นได้, และส่งเป็น argument ได้เหมือน value ทั่วไป ข้อเท็จจริงเดียวนี้คือเหตุผลที่เกือบทุกบทเรียนหลังจากนี้อธิบายได้ด้วย “ชื่อนี้อ้างถึง object ไหน และเกิดอะไรขึ้นเมื่อเรียก method หรือ operator กับมัน” — ไม่มีกฎแยกต่างหากสำหรับ “primitive” กับ “object”
คอร์สนี้สอนให้คุณอ่านโค้ดที่ AI เขียนอย่างไร
หัวข้อที่มีชื่อว่า “คอร์สนี้สอนให้คุณอ่านโค้ดที่ AI เขียนอย่างไร”ทุกบทเรียนตั้งแต่นี้ไปจะจบด้วยสองอย่างที่มุ่งเป้าไปที่โค้ดที่คุณไม่ได้เขียนเอง: checklist สั้น ๆ “อ่านโค้ดที่ AI เขียน” และแบบฝึกหัด <SpotTheBug> อย่างน้อยหนึ่งอันที่มีโค้ดที่ดูสมเหตุสมผลแต่พังจริงพร้อมคำอธิบายซ่อนอยู่ bug ในแบบฝึกหัดเหล่านั้นไม่ได้แต่งขึ้นลอย ๆ — มันคือรูปแบบเดิม ๆ ไม่กี่แบบ (mutable default argument, is เทียบกับ ==, aliasing, ลืม is None, การ shadow builtin) ที่พบซ้ำ ๆ ในโค้ด Python ที่ AI สร้าง เพราะ language model สร้างรูปร่างของโค้ดที่คุ้นเคยซ้ำ โดยไม่จำเป็นต้องผ่าน object model และ scoping semantics เฉพาะของ Python จริง ๆ ให้ใช้ทุก checklist เป็นรายการ “สิ่งที่ต้อง grep หา” ก่อนเชื่อ diff ไหนก็ตาม ไม่ใช่แค่เนื้อหาสอบ
เนื้อหาในโมดูลนี้
หัวข้อที่มีชื่อว่า “เนื้อหาในโมดูลนี้”| บทเรียน | แนวคิดหลัก |
|---|---|
| Variables & Types | Name เทียบกับ object, is เทียบกับ ==, mutability, truthiness |
| Control Flow | if/elif/else, match, iterator protocol, for, while |
| Functions | def, mutable default, *args/**kwargs, LEGB scope |
| Strings | Methods, slicing, bytes เทียบกับ str, f-strings, immutability |
แต่ละบทเรียนเป็นอิสระและจบด้วย playground ที่รันได้, callout, แบบทดสอบ, และจุดตรวจสอบความก้าวหน้า
สคริปต์ Python แรกของคุณ
หัวข้อที่มีชื่อว่า “สคริปต์ Python แรกของคุณ”สคริปต์ด้านล่างแสดงไวยากรณ์หลักที่คุณจะพบในทุกบทเรียน: ตัวแปร, ฟังก์ชัน, ลูป, และ f-string
def describe(value: int) -> str: """Return a short description of a number.""" if value < 0: label = "negative" elif value == 0: label = "zero" else: label = "positive" return f"{value} is {label}"
numbers = [-3, 0, 7, 42]for n in numbers: print(describe(n))
total = sum(numbers)print(f"Sum: {total}")print("First program complete.")Loading Python runtime (first run only)…
Playground ที่สองแสดง thesis “ทุกอย่างคือ object” ในทางปฏิบัติ รวมถึงรูปแบบการตรวจสอบที่ดูสมเหตุสมผลแต่พังทันทีที่มี subclass เข้ามาเกี่ยวข้อง
# 1. Everything is an object -- including functions, types, and type itself.print(type(42))print((42).bit_length())print(type(print))print(type(int))print(type(type))
# 2. type(x) == SomeClass vs isinstance(x, SomeClass) with a subclass.class Animal: pass
class Dog(Animal): pass
fido = Dog()print(type(fido) == Animal) # BUG-prone check: False, even though Dog IS an Animalprint(isinstance(fido, Animal)) # True -- respects subclassingLoading Python runtime (first run only)…
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”flowchart LR X["name: x"] --> ObjX["object (id, type, value)"] Describe["name: describe"] --> ObjDescribe["function object (also has id, type, value)"] Int["name: int"] --> ObjInt["class object, itself an instance of type"] Type["name: type"] --> ObjType["the class of every class, an instance of itself"]
กล่องทุกกล่องทางขวามือคือสิ่งเดียวกัน — object ที่มี identity และ type — ไม่ว่าชื่อทางซ้ายจะอ้างถึงสิ่งที่เรียกกันทั่วไปว่า “ข้อมูล” หรือ “โค้ด” การอ่าน Python ให้คล่องคือการถามสำหรับทุกชื่อว่า “ตอนนี้มันอ้างถึง object ไหน” แทนที่จะแยกชื่อออกเป็นหมวดความคิดคนละกลุ่มระหว่าง primitive กับ object
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| สิ่งที่ได้ | ประโยชน์ | ต้นทุน |
|---|---|---|
| Object model แบบเดียวกันทั้งหมด | mental model เดียวครอบคลุมตัวเลข, function, class, และ module | ไม่มี “fast path” พิเศษสำหรับ primitive — ทุกอย่างมี overhead ของ object |
type() เผยชนิดที่แน่นอน | introspection ที่แม่นยำไม่กำกวม | การเทียบ type(x) == SomeClass ปฏิเสธ subclass ที่ถูกต้องแบบเงียบ ๆ |
| ปรัชญาเน้นความอ่านง่าย (PEP 20) | มีวิธีที่ยอมรับกันและค้นหาได้สำหรับเกือบทุกอย่าง | บางครั้งแลกประสิทธิภาพดิบหรือความกระชับเพื่อความชัดเจน |
| การเยื้อง 4 ช่องว่างเป็น syntax | บังคับโครงสร้าง block ที่อ่านง่ายสม่ำเสมอทั้ง codebase | ผสม tab กับ space เป็น error จริง (TabError) ไม่ใช่แค่ข้อบกพร่องด้าน style |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- “ตัวเลข, string, และ boolean เป็น primitive; มีแต่ class ที่กำหนดเองเท่านั้นที่เป็น object” ทั้งหมดล้วนเป็น object —
type(42)คือintซึ่งเป็น class จริง และ(42).bit_length()พิสูจน์ว่ามัน methods เหมือน object อื่น - “
type(x) == SomeClassเหมือนกับisinstance(x, SomeClass)” ไม่จริง —type(x) == SomeClassเป็นFalseสำหรับ instance ของ subclass ใด ๆ ในขณะที่isinstanceเคารพลำดับชั้น class ทั้งหมด - “กฎความอ่านง่ายของ Python (PEP 8/PEP 20) เป็นแค่ความชอบด้าน style ไม่มีผลจริง” มันกำหนดว่าอะไร “ดูถูกต้อง” มากพอจนความเบี่ยงเบนมักเป็นจุดที่ bug ซ่อนอยู่ — codebase ที่ทำตามวิธีเดียวที่ชัดเจนอ่านหา bug ได้ง่ายกว่า ไม่ว่าจะเป็นมนุษย์หรือ AI เขียน
- “function หรือ class ไม่ใช่ value จริง ๆ เหมือนตัวเลข” ทั้งคู่เป็น object ที่กำหนดให้ตัวแปรได้, เก็บใน list ได้, หรือส่งเป็น argument ได้ —
type(print)และtype(int)พิสูจน์ว่าไม่มีตัวไหนเป็น syntax พิเศษ
อ่านโค้ดที่ AI เขียน
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI เขียน”- การตรวจ type ใช้
type(x) == SomeClassทั้งที่ตั้งใจจะใช้isinstance(x, SomeClass)หรือเปล่า? แบบแรกปฏิเสธ subclass ที่ถูกต้องแบบเงียบ ๆ - โค้ดสมมติว่า “primitive” (ตัวเลข, boolean, string) ทำงานต่างจาก “object จริง” หรือเปล่า เช่นสมมติว่ามันไม่มี methods หรือ subclass ไม่ได้?
- ทุกครั้งที่บทเรียนตั้งแต่นี้ไปให้ checklist มา โค้ดที่กำลังตรวจจริง ๆ ถูกเช็คกับมันหรือเปล่า หรือหยุดแค่ “มันรันได้ไหม”?
- function หรือ class ถูกปฏิบัติเหมือนไม่ใช่ value ธรรมดาหรือเปล่า เช่นไม่เคยถูกกำหนดให้ตัวแปรหรือส่งเป็น argument ทั้งที่ทำแบบนั้นจะทำให้โค้ดง่ายขึ้น?
🐛 หา bug ในโค้ดนี้
def is_valid_item(item): if type(item) == dict: return "id" in item return Falseเฉลย
type(item) == dict รับเฉพาะค่าที่ type ตรงเป๊ะกับ dict เท่านั้น — subclass ของ dict ที่สมเหตุสมผล (เช่นที่ library คืนมา, หรือ collections.UserDict, หรือ class Config(dict): ... ที่ developer นิยามเอง) จะไม่ผ่านการตรวจนี้ ทั้งที่มันทำตัวเหมือน dict ทุกอย่างที่สำคัญตรงนี้ isinstance(item, dict) ตรวจลำดับชั้น class ทั้งหมดและรับ subclass ด้วย ทางแก้: if isinstance(item, dict): return "id" in item