การรวมข้อมูลและเมธอดไว้ด้วยกัน และจำกัดการเข้าถึงข้อมูลภายในจากภายนอกโดยตรง (เช่น การใช้ private variables)
การสร้างคลาสใหม่จากคลาสที่มีอยู่เดิมเพื่อนำโค้ดกลับมาใช้ใหม่ (ความสัมพันธ์แบบ "Is-A")
ความสามารถของวัตถุต่างชนิดในการตอบสนองต่อการเรียกเมธอดเดียวกันในรูปแบบที่ต่างกัน (เช่น Method Overriding)
การซ่อนรายละเอียดการทำงานที่ซับซ้อน และแสดงเฉพาะสิ่งที่จำเป็นต้องใช้งาน (เช่น Abstract Classes)
ผูกพันกันแน่นแฟ้น (Tightly coupled) ใช้เมื่อคลาสลูกเป็นรูปแบบเฉพาะของคลาสแม่
class Car(Vehicle):
pass
ยืดหยุ่นกว่า (Loosely coupled) ใช้เมื่อวัตถุที่ซับซ้อนประกอบขึ้นจากวัตถุอื่น ๆ
class Car:
def __init__(self):
self.engine = Engine()
.gitignore สำหรับ Python
project/, week09/, week10/
การรวมข้อมูลและเมธอดเข้าด้วยกันเป็นหน่วยเดียว และจำกัดการเข้าถึงสถานะภายในของวัตถุ (Information Hiding)
_Class__var
class BankAccount:
def __init__(self, balance):
self._balance = balance # Protected
self.__secret = "Key" # Private
def deposit(self, amount):
if amount > 0:
self._balance += amount
account = BankAccount(100)
# account.__secret <-- AttributeError
# account._BankAccount__secret <-- เข้าถึงได้ถ้ารู้ชื่อ
เรียกใช้เมธอดเสมือนเป็น Attribute ตัวหนึ่ง
@property
def radius(self):
return self._radius
เพิ่มลอจิกตรวจสอบความถูกต้องเมื่อมีการกำหนดค่า
@radius.setter
def radius(self, value):
if value < 0: raise ValueError
self._radius = value
คลาสลูก (Child) รับคุณลักษณะและเมธอดมาจากคลาสแม่ (Parent) สามารถ ขยาย (extend) หรือ เขียนทับ (override) การทำงานได้
super()ส่งคืนออบเจกต์ตัวแทนเพื่อเรียกใช้เมธอดของคลาสแม่ จำเป็นมากสำหรับการเรียกใช้เมธอดที่ถูก Override
class Animal:
def speak(self):
print("เสียงสัตว์ทั่วไป")
class Dog(Animal): # สืบทอด
def speak(self):
# เขียนทับ (Override)
print("โฮ่ง!")
class SuperDog(Dog):
def speak(self):
# เรียกใช้ speak() ของคลาสแม่
super().speak()
print("...และบินได้ด้วย!")
Python รองรับการสืบทอดจากหลายคลาสแม่ ลำดับมีความสำคัญเพื่อหลีกเลี่ยง "Diamond Problem"
อัลกอริทึม (C3 Linearization) ที่ Python ใช้กำหนดลำดับในการค้นหาเมธอด
Python ค้นหาจาก ซ้ายไปขวา, ลึกไปตื้น (โดยส่วนใหญ่)
class A:
def do_task(self): print("A")
class B(A): pass
class C(A):
def do_task(self): print("C")
# Multiple Inheritance
class D(B, C): pass
d = D()
d.do_task()
# ผลลัพธ์: "C"
# MRO: D -> B -> C -> A
ความสามารถของวัตถุต่างชนิดในการตอบสนองต่อการเรียกเมธอดเดียวกัน ใน Python มักใช้หลักการ Duck Typing
class PDFReader:
def read(self):
return "Reading PDF..."
class WordReader:
def read(self):
return "Reading Doc..."
Abstraction ช่วยให้เรากำหนดโครงร่าง (Interface) โดยซ่อนรายละเอียดการทำงานที่ซับซ้อนไว้ บังคับให้คลาสลูกต้องปฏิบัติตามสัญญาที่ระบุไว้
abc)from abc import ABC, abstractmethod
class PaymentGateway(ABC):
@abstractmethod
def process(self, amount):
pass
class PayPal(PaymentGateway):
def process(self, amount):
print(f"Paid ${amount} via PayPal")
# pg = PaymentGateway() # Error! สร้างไม่ได้
p = PayPal()
p.process(100) # Output: Paid $100 via PayPal
ใช้เมื่อคลาสลูกเป็น "รูปแบบเฉพาะ" ของคลาสแม่
ผูกมัดแน่น (Rigid) การเปลี่ยนแปลงในคลาสแม่กระทบลูกทั้งหมด
ใช้สร้างวัตถุที่ซับซ้อนจากส่วนประกอบย่อยๆ ที่เป็นอิสระต่อกัน
ยืดหยุ่นกว่า (Flexible) สามารถสลับเปลี่ยนส่วนประกอบได้ขณะทำงาน
วางแผนโครงสร้างคลาสก่อนเริ่มเขียนโค้ดจริง
หลีกเลี่ยงการเขียนโค้ดทั้งหมดในไฟล์เดียว (Monolithic) ควรแบ่งโค้ดเป็นส่วนๆ ตามหน้าที่
.py แต่ละไฟล์ที่มีคลาสหรือฟังก์ชันที่เกี่ยวข้องกัน
__init__.py ใช้รวมหลายโมดูล
main.py หรือ app.py สำหรับเริ่มโปรแกรม
git init
git remote add origin <url>
git add .
git commit -m "เพิ่มฟีเจอร์ User class"
git pull origin main
git push origin main
สิ่งที่ไม่ควรอัปโหลด:
__pycache__/venv/ หรือไฟล์ .env.DS_Store)เมธอดปกติ ทำงานกับข้อมูลของออบเจกต์ (State)
self
def work(self):
self.data = 1
ผูกกับคลาส ไม่ใช่อินแตนซ์ มักใช้สร้าง Factory Methods
@classmethodcls
@classmethod
def from_string(cls, s):
return cls(s)
ฟังก์ชันยูทิลิตี้ที่อยู่ในคลาสแต่ไม่ยุ่งกับข้อมูลภายใน
@staticmethod
@staticmethod
def is_valid(x):
return x > 0
หลีกเลี่ยงการเขียน get_variable() และ set_variable() แบบ Java ให้ใช้ Property เพื่อตรวจสอบข้อมูลหรือคำนวณค่าโดยไม่ต้องเปลี่ยน Interface เดิม
area จาก width * height)class Circle: def __init__(self, radius): self._radius = radius @property def diameter(self): # แอททริบิวต์แบบคำนวณ return self._radius * 2 @diameter.setter def diameter(self, value): # ตรวจสอบความถูกต้อง if value < 0: raise ValueError self._radius = value / 2
กำหนดพฤติกรรมของออบเจกต์เมื่อทำงานร่วมกับตัวดำเนินการ (Operators) และฟังก์ชันของ Python
สัปดาห์หน้า (Week 10) เราจะเปลี่ยนจาก "เขียนคลาสอย่างไร" ไปสู่ "ออกแบบระบบอย่างไร" เราจะเรียนรู้หลักการ SOLID และ Design Patterns มาตรฐาน
5 หลักการออกแบบเพื่อให้ซอฟต์แวร์เข้าใจง่าย ยืดหยุ่น และดูแลรักษาง่าย
วิธีการแก้ปัญหามาตรฐานที่ใช้ซ้ำได้ (เช่น Singleton, Factory, Observer)
Scoop เป็นคำสั่งติดตั้งซอฟต์แวร์แบบ command-line สำหรับ Windows จะใช้เพื่อจัดการเครื่องมือพัฒนาต่างๆ
เปิด PowerShell (ไม่ใช่ CMD) ในฐานะ Administrator
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm get.scoop.sh | iex
เราต้องการ Git สำหรับการจัดการเวอร์ชัน และ uv สำหรับจัดการแพ็คเกจ Python ที่มีความเร็วสูงมาก
Homebrew เป็นตัวจัดการแพ็คเกจ (Package Manager) ยอดนิยมสำหรับ macOS (คล้าย Scoop ของ Windows)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
*หมายเหตุ: หลังติดตั้งเสร็จ อาจต้องทำตามคำแนะนำบนหน้าจอเพื่อเพิ่ม Homebrew เข้าสู่ PATH
ใช้ Homebrew ติดตั้ง Git และ uv (Python Package Manager ความเร็วสูง)
brew install git uv
git --version
uv --version
เราจะสร้างโฟลเดอร์สำหรับโปรเจกต์และเริ่มต้นสภาพแวดล้อม Python โดยใช้ `uv`
สร้างไฟล์ชื่อ main.py เริ่มต้นด้วยการสืบทอดจาก QMainWindow
import sys from PySide6.QtWidgets import QApplication, QMainWindow # 1. สร้างคลาสย่อยของ QMainWindow เพื่อสร้างหน้าต่างแอป class BrowserWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("My PySide6 Browser") self.resize(1024, 768) # 2. ตั้งค่าลูปการทำงานของแอปพลิเคชัน (Application Loop) if __name__ == "__main__": app = QApplication(sys.argv) window = BrowserWindow() window.show() sys.exit(app.exec())
QWebEngineView เป็นวิดเจ็ตทรงพลังที่ทำงานบนพื้นฐานของ Chromium
QWebEngineViewQUrl สำหรับจัดการ URLfrom PySide6.QtWebEngineWidgets import QWebEngineView from PySide6.QtCore import QUrl class BrowserWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("My PySide6 Browser") # สร้าง Web View self.browser = QWebEngineView() # กำหนด URL เริ่มต้น self.browser.setUrl(QUrl("http://www.google.com")) # วางไว้ตรงกลางหน้าต่าง self.setCentralWidget(self.browser)
เราต้องการแถบเครื่องมือด้านบน ใน Qt เราจะเพิ่ม QAction ลงใน QToolBar
from PySide6.QtWidgets import QToolBar, QStyle # ภายใน __init__ ... # สร้าง Toolbar nav_bar = QToolBar() self.addToolBar(nav_bar) # สร้างปุ่มคำสั่ง (Actions) back_btn = QAction("Back", self) back_btn.triggered.connect(self.browser.back) nav_bar.addAction(back_btn) forward_btn = QAction("Forward", self) forward_btn.triggered.connect(self.browser.forward) nav_bar.addAction(forward_btn) reload_btn = QAction("Reload", self) reload_btn.triggered.connect(self.browser.reload) nav_bar.addAction(reload_btn)
QAction จาก PySide6.QtGui
เราต้องการช่องกรอกข้อความ (QLineEdit) เพื่อพิมพ์ URL และเชื่อมต่อกับเบราว์เซอร์
from PySide6.QtWidgets import QLineEdit # ภายใน __init__... self.url_bar = QLineEdit() self.url_bar.returnPressed.connect(self.navigate_to_url) nav_bar.addWidget(self.url_bar) # ซิงค์ URL เมื่อเว็บเปลี่ยน self.browser.urlChanged.connect(self.update_url) # สร้างเมธอดใหม่ในคลาส def navigate_to_url(self): url = self.url_bar.text() if not url.startswith("http"): url = "http://" + url self.browser.setUrl(QUrl(url)) def update_url(self, q): self.url_bar.setText(q.toString())
รันโปรแกรมเพื่อตรวจสอบความถูกต้องของสภาพแวดล้อม