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

พื้นฐานและไวยากรณ์

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 ครั้งเดียวแล้วลืมได้เลย

นี่คือแนวคิดเดียวที่บทเรียนที่เหลือในคอร์สนี้จะย้อนกลับมาซ้ำ ๆ: ใน 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 object
print(type(print)) # <class 'builtin_function_or_method'>
print(type(int)) # <class 'type'> -- int itself is an instance of type
print(type(type)) # <class 'type'> -- type is an instance of itself

int ไม่ใช่ 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 & TypesName เทียบกับ object, is เทียบกับ ==, mutability, truthiness
Control Flowif/elif/else, match, iterator protocol, for, while
Functionsdef, mutable default, *args/**kwargs, LEGB scope
StringsMethods, slicing, bytes เทียบกับ str, f-strings, immutability

แต่ละบทเรียนเป็นอิสระและจบด้วย playground ที่รันได้, callout, แบบทดสอบ, และจุดตรวจสอบความก้าวหน้า

สคริปต์ด้านล่างแสดงไวยากรณ์หลักที่คุณจะพบในทุกบทเรียน: ตัวแปร, ฟังก์ชัน, ลูป, และ 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.")

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 Animal
print(isinstance(fido, Animal)) # True -- respects subclassing
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 ที่มี id, type และ value -- รวมถึงฟังก์ชันและคลาสด้วย

กล่องทุกกล่องทางขวามือคือสิ่งเดียวกัน — 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 พิเศษ
  • การตรวจ 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

โค้ดนี้พิมพ์อะไร?
class Animal:
    pass

class Dog(Animal):
    pass

fido = Dog()
print(type(fido) == Animal)
print(isinstance(fido, Animal))
โค้ดนี้พิมพ์อะไร?
print(type(int))
print(type(type))
PEP 20 หรือ 'The Zen of Python' เน้นอะไรมากที่สุด?
Python กำหนดขอบเขตของบล็อกโค้ด เช่น ฟังก์ชันและ if-statement อย่างไร?
ใน Python, ข้อใดถูกต้องเกี่ยวกับจำนวนเต็ม, สตริง, และฟังก์ชัน?