จัดโครงสร้างโค้ดของคุณด้วยคลาสที่ออกแบบมาอย่างดี: การเริ่มต้นค่า แอตทริบิวต์ของอินสแตนซ์หรือของคลาส การสืบทอดและข้อแลกเปลี่ยนของมัน
เปิดบทเรียนนี้ใน Kodokonคุณทำงานกับอ็อบเจกต์ของ Python อยู่ตลอดเวลา ไม่ว่าจะเป็นสายอักขระ ลิสต์ หรือไฟล์ การเขียนคลาสของตัวเองคือการรวม สถานะ (แอตทริบิวต์) และ พฤติกรรม (เมทอด) ไว้เบื้องหลังอินเทอร์เฟซที่สอดคล้องกัน มีจุดหนึ่งเรื่องคำศัพท์ที่มักเข้าใจผิดกัน: __init__ ไม่ใช่คอนสตรักเตอร์ อ็อบเจกต์มีอยู่แล้ว (ถูกสร้างโดย __new__) ตั้งแต่ตอนที่ __init__ ได้รับ self หน้าที่ของมันคือ เริ่มต้นค่า ให้อินสแตนซ์ และ self ก็ไม่มีอะไรวิเศษเลย มันคือพารามิเตอร์ตัวแรกของทุกเมทอดของอินสแตนซ์ ซึ่ง Python ส่งให้อัตโนมัติตอนเรียกใช้
class Account:
def __init__(self, owner, balance=0.0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("amount must be > 0")
self.balance += amount
return self.balance
def __repr__(self):
return f"Account({self.owner!r}, {self.balance})"
account = Account("Ada")
account.deposit(120.0)
print(account)ความแตกต่างที่สำคัญยิ่งในงานจริง: แอตทริบิวต์ของคลาส ถูกประกาศไว้ในตัวคลาสและถูกใช้ร่วมกันโดยทุกอินสแตนซ์ ส่วน แอตทริบิวต์ของอินสแตนซ์ ถูกสร้างบน self โดยปกติภายใน __init__ เวลาอ่านค่า Python จะมองที่อินสแตนซ์ก่อน แล้วจึงย้อนไปที่คลาส และต่อไปยังคลาสแม่ การกำหนดค่า self.x = ... จะสร้างแอตทริบิวต์ของอินสแตนซ์เสมอ ซึ่งจะบดบังแอตทริบิวต์ของคลาสที่ชื่อเดียวกัน กับดักคลาสสิกในโลกจริงคือ แอตทริบิวต์ของคลาสที่ เปลี่ยนแปลงได้ (mutable)
class Basket:
items = []
def add(self, item):
self.items.append(item)
a = Basket()
b = Basket()
a.add("apple")
print(b.items) # ['apple']: shared list!การสืบทอดสื่อถึงความสัมพันธ์แบบ "เป็นชนิดหนึ่งของ": Car เป็น Vehicle ชนิดหนึ่ง คลาสลูกสืบทอดเมทอดมา สามารถเขียนทับได้ และ super() ทำให้มันเรียกเวอร์ชันของคลาสแม่ได้ ซึ่งขาดไม่ได้ใน __init__ เพื่อไม่ให้ลัดวงจรการเริ่มต้นค่าของคลาสแม่ Python แก้ปัญหาการเรียกด้วยการไล่ตาม MRO (Method Resolution Order) อย่าหยิบการสืบทอดมาใช้แบบอัตโนมัติทุกครั้ง หากต้องการนำโค้ดกลับมาใช้ซ้ำโดยไม่มีความสัมพันธ์แบบ "เป็นชนิดหนึ่งของ" ให้เลือก การประกอบ (composition) แทน (อ็อบเจกต์หนึ่งถือครองอีกอ็อบเจกต์หนึ่งไว้) ลำดับชั้นที่ลึกนั้นเปราะบาง คลาสแม่แต่ละตัวจะกลายเป็น API โดยปริยายสำหรับลูกทั้งหมดของมัน
class Vehicle:
def __init__(self, brand):
self.brand = brand
def describe(self):
return self.brand
class Car(Vehicle):
def __init__(self, brand, doors):
super().__init__(brand)
self.doors = doors
def describe(self):
base = super().describe()
return f"{base}, {self.doors} doors"
print(Car("Aster", 5).describe())