🐍 Python Programming Tutorial

การเขียนโปรแกรม Python สำหรับคนไทย
From Simple Scripts to Advanced Generators

บทนำ (Introduction)

ในบทเรียนนี้ เราจะเรียนรู้วิธีการพัฒนาโค้ด Python ตั้งแต่สคริปต์ง่ายๆ ไปจนถึงการใช้ Generator ใน Class เราจะสร้างโปรแกรมอ่านไฟล์ที่พัฒนาขึ้นทีละขั้นตอน เพื่อให้เข้าใจหลักการเขียนโค้ดที่ดีและยืดหยุ่น

In this tutorial, we'll learn how to develop Python code from simple scripts to using Generators in Classes. We'll build a file reader program that evolves step by step to understand good and flexible coding principles.

Step 1: สคริปต์พื้นฐาน (Basic Script)

📝 ขั้นตอนที่ 1: เขียนโค้ดที่ทำงานจากบนลงล่าง (Top to Bottom)

เริ่มต้นด้วยสคริปต์ง่ายๆ ที่อ่านไฟล์และแสดงผล ไม่มีการจัดระเบียบโครงสร้าง

Let's start with a simple script that reads a file and displays output, without any structural organization.

# simple_reader.py # อ่านไฟล์ data.txt และแสดงแต่ละบรรทัด # Read data.txt file and display each line with open('data.txt', 'r', encoding='utf-8') as file: for line in file: print(line.strip())

❌ ปัญหา (Problems)

  • ไม่สามารถนำกลับมาใช้ใหม่ได้ (Not reusable) - ต้องคัดลอกโค้ดทุกครั้ง
  • ชื่อไฟล์ฮาร์ดโค้ด (Hardcoded filename) - เปลี่ยนไฟล์ไม่ได้
  • ไม่มี error handling - ถ้าไฟล์ไม่มีจะเกิด error ทันที
  • ยากต่อการทดสอบ (Hard to test) - ไม่สามารถ import ไปใช้ที่อื่นได้

Step 2: เพิ่ม Entry Point

📝 ขั้นตอนที่ 2: ใช้ if __name__ == "__main__":

เพิ่มส่วน entry point เพื่อให้โค้ดของเราสามารถ import ไปใช้ในไฟล์อื่นได้ โดยไม่ทำงานอotomatically

Add an entry point so our code can be imported into other files without running automatically.

📚 คำศัพท์สำคัญ (Key Vocabulary)

__name__ = ชื่อของ module (module name)

__main__ = ค่าของ __name__ เมื่อไฟล์ถูกรันโดยตรง (value when file is run directly)

Entry Point = จุดเริ่มต้นของโปรแกรม (program starting point)

# reader_with_entry.py # เพิ่ม entry point เพื่อให้สามารถ import ได้ # Add entry point to make it importable with open('data.txt', 'r', encoding='utf-8') as file: for line in file: print(line.strip()) if __name__ == "__main__": # โค้ดนี้จะทำงานเฉพาะเมื่อรันไฟล์โดยตรง # This code runs only when file is executed directly print("โปรแกรมทำงานเสร็จสิ้น (Program completed)")

✅ ประโยชน์ (Benefits)

  • สามารถ import ได้ - ใช้โค้ดในไฟล์อื่นได้โดยไม่รันทันที
  • แยก testing ได้ - เขียน test ได้ง่ายขึ้น
  • โครงสร้างชัดเจน - รู้ว่าส่วนไหนคือจุดเริ่มต้น

Step 3: สร้าง Function

📝 ขั้นตอนที่ 3: ห่อหุ้มโค้ดด้วย function

ย้ายโค้ดเข้าไปใน function เพื่อให้สามารถเรียกใช้ซ้ำได้ และรับพารามิเตอร์ชื่อไฟล์

Move code into a function to make it reusable and accept filename as a parameter.

# reader_with_function.py # ใช้ function เพื่อให้สามารถเรียกใช้ซ้ำได้ # Use function to make code reusable def read_file(filename): """ อ่านไฟล์และแสดงผลแต่ละบรรทัด Read file and display each line Parameters (พารามิเตอร์): filename (str): ชื่อไฟล์ที่ต้องการอ่าน (file name to read) """ try: with open(filename, 'r', encoding='utf-8') as file: for line in file: print(line.strip()) except FileNotFoundError: print(f"ไม่พบไฟล์: {filename} (File not found)") except Exception as e: print(f"เกิดข้อผิดพลาด (Error occurred): {e}") if __name__ == "__main__": # เรียกใช้ function กับไฟล์ต่างๆ # Call function with different files read_file('data.txt') read_file('config.txt')

✅ ข้อดี (Advantages)

  • Reusable - เรียกใช้ได้หลายครั้งโดยไม่ต้องเขียนโค้ดซ้ำ
  • Flexible - รับชื่อไฟล์เป็น parameter ได้
  • Error Handling - จัดการข้อผิดพลาดได้
  • Documented - มี docstring อธิบายการใช้งาน

Step 4: จัดระเบียบด้วย Class

📝 ขั้นตอนที่ 4: สร้าง Class พร้อม __init__

สร้าง Class เพื่อเก็บข้อมูลและพฤติกรรมที่เกี่ยวข้องไว้ด้วยกัน โดยใช้ __init__ ตั้งค่าเริ่มต้น

Create a Class to group related data and behavior together, using __init__ for initialization.

📚 คำศัพท์ OOP (Object-Oriented Programming)

Class = แม่แบบสำหรับสร้าง object (blueprint for creating objects)

__init__ = Constructor - ฟังก์ชันที่รันเมื่อสร้าง object ใหม่

self = อ้างอิงถึง instance ปัจจุบัน (reference to current instance)

Method = ฟังก์ชันภายใน class (function inside a class)

# reader_with_class.py # ใช้ Class เพื่อจัดระเบียบโค้ดและเก็บ state # Use Class to organize code and maintain state class FileReader: """ คลาสสำหรับอ่านไฟล์ข้อความ Class for reading text files """ def __init__(self, filename, encoding='utf-8'): """ สร้าง FileReader object ใหม่ Initialize a new FileReader object Parameters: filename (str): ชื่อไฟล์ (file name) encoding (str): การเข้ารหัสไฟล์ (file encoding) """ self.filename = filename self.encoding = encoding self.lines_read = 0 # นับจำนวนบรรทัดที่อ่าน (count lines read) def read_and_print(self): """อ่านไฟล์และแสดงผล (Read and display file)""" try: with open(self.filename, 'r', encoding=self.encoding) as file: for line in file: print(line.strip()) self.lines_read += 1 print(f"\nอ่านทั้งหมด {self.lines_read} บรรทัด (Read {self.lines_read} lines)") except FileNotFoundError: print(f"ไม่พบไฟล์: {self.filename}") except Exception as e: print(f"เกิดข้อผิดพลาด: {e}") def get_stats(self): """คืนค่าสถิติการอ่านไฟล์ (Return file reading statistics)""" return { 'filename': self.filename, 'lines_read': self.lines_read, 'encoding': self.encoding } if __name__ == "__main__": # สร้าง object และใช้งาน (Create object and use it) reader = FileReader('data.txt') reader.read_and_print() # ดูสถิติ (View statistics) stats = reader.get_stats() print(f"\nสถิติ (Statistics): {stats}")

✅ ทำไมต้องใช้ Class? (Why Use Classes?)

  • Encapsulation - รวมข้อมูลและฟังก์ชันที่เกี่ยวข้องไว้ด้วยกัน
  • State Management - เก็บสถานะของ object (เช่น lines_read)
  • Reusability - สร้าง object หลายตัวได้ แต่ละตัวมี state ของตัวเอง
  • Organization - โค้ดมีโครงสร้างชัดเจน อ่านง่าย บำรุงรักษาง่าย
  • Extensibility - ขยายความสามารถได้ง่ายผ่าน inheritance

Step 5: Generator Function - พลังที่แท้จริง

📝 ขั้นตอนที่ 5: ใช้ Generator สำหรับ Memory Efficiency

Generator ช่วยให้เราอ่านไฟล์ขนาดใหญ่ได้โดยไม่ต้องโหลดทั้งหมดเข้า memory พร้อมกัน ใช้คำสั่ง yield แทน return

Generators allow us to read large files without loading everything into memory at once. Use yield instead of return.

📚 Generator คืออะไร? (What is a Generator?)

Generator = ฟังก์ชันพิเศษที่ใช้ yield เพื่อคืนค่าทีละตัว (special function that uses yield to return values one at a time)

yield = คืนค่าแล้วหยุดชั่วคราว รอเรียกใช้ครั้งถัดไป (return value and pause, waiting for next call)

Lazy Evaluation = ประมวลผลเมื่อต้องการใช้จริง ไม่ใช่ทั้งหมดตั้งแต่แรก (process when needed, not all at once)

# reader_with_generator.py # ใช้ Generator เพื่อประหยัด memory และรองรับไฟล์ขนาดใหญ่ # Use Generator for memory efficiency and large file support class FileReader: """ คลาสสำหรับอ่านไฟล์แบบ memory-efficient Class for memory-efficient file reading """ def __init__(self, filename, encoding='utf-8', chunk_size=1024): """ Initialize FileReader Parameters: filename (str): ชื่อไฟล์ (file name) encoding (str): การเข้ารหัส (encoding) chunk_size (int): ขนาดข้อมูลที่อ่านครั้งละชิ้น (bytes per chunk) """ self.filename = filename self.encoding = encoding self.chunk_size = chunk_size self.total_lines = 0 self.total_bytes = 0 def read_lines(self): """ Generator ที่คืนค่าแต่ละบรรทัดทีละบรรทัด Generator that yields one line at a time Yields: str: บรรทัดของไฟล์ (line from file) """ try: with open(self.filename, 'r', encoding=self.encoding) as file: for line in file: self.total_lines += 1 self.total_bytes += len(line.encode(self.encoding)) yield line.strip() # คืนค่าทีละบรรทัด (yield one line) except FileNotFoundError: yield f"ERROR: ไม่พบไฟล์ {self.filename}" except Exception as e: yield f"ERROR: {e}" def read_chunks(self): """ Generator ที่อ่านไฟล์ทีละชิ้นตามขนาดที่กำหนด Generator that reads file in fixed-size chunks Yields: str: ชิ้นส่วนของไฟล์ (chunk of file) """ try: with open(self.filename, 'r', encoding=self.encoding) as file: while True: chunk = file.read(self.chunk_size) if not chunk: break self.total_bytes += len(chunk.encode(self.encoding)) yield chunk except Exception as e: yield f"ERROR: {e}" def filter_lines(self, keyword): """ Generator ที่กรองเฉพาะบรรทัดที่มีคำที่ต้องการ Generator that filters lines containing a keyword Parameters: keyword (str): คำที่ต้องการค้นหา (keyword to search) Yields: str: บรรทัดที่ตรงเงื่อนไข (matching line) """ for line in self.read_lines(): if keyword.lower() in line.lower(): yield line def get_stats(self): """คืนค่าสถิติ (Return statistics)""" return { 'filename': self.filename, 'total_lines': self.total_lines, 'total_bytes': self.total_bytes, 'encoding': self.encoding } if __name__ == "__main__": # ตัวอย่างที่ 1: อ่านทีละบรรทัด (Example 1: Read line by line) print("=== อ่านทีละบรรทัด (Reading Line by Line) ===") reader = FileReader('data.txt') for line in reader.read_lines(): print(line) if reader.total_lines >= 5: # จำกัดแสดงแค่ 5 บรรทัด break print(f"\nสถิติ: {reader.get_stats()}") # ตัวอย่างที่ 2: กรองข้อมูล (Example 2: Filter data) print("\n=== กรองบรรทัดที่มีคำว่า 'python' ===") reader2 = FileReader('data.txt') for line in reader2.filter_lines('python'): print(f"พบ: {line}") # ตัวอย่างที่ 3: อ่านแบบ chunks (Example 3: Read in chunks) print("\n=== อ่านทีละ chunk ===") reader3 = FileReader('data.txt', chunk_size=50) for i, chunk in enumerate(reader3.read_chunks(), 1): print(f"Chunk {i}: {chunk[:50]}...") if i >= 3: break

เปรียบเทียบ: Normal Function vs Generator

มาดูความแตกต่างระหว่างการใช้ function ธรรมดากับ generator อย่างละเอียด และทำไม generator จึงทรงพลังกว่า

Let's compare in detail the difference between using a normal function and a generator, and why generators are more powerful.

🔴 ปัญหาของ Normal Function (The Problem with Normal Functions)

# ❌ Normal Function - โหลดทั้งหมดเข้า memory ทันที! # ❌ Normal Function - Loads EVERYTHING into memory immediately! def read_all_lines(filename): with open(filename, 'r') as file: lines = file.readlines() # 💥 โหลดทั้งไฟล์เข้า RAM ทันที! return lines # 💥 คืนค่าเป็น list ทั้งหมด! # สถานการณ์ที่ 1: ไฟล์ขนาด 1 GB # Scenario 1: 1 GB file all_lines = read_all_lines('log_file_1gb.txt') # 💀 ใช้แรม ~1 GB สำหรับเก็บ list ทั้งหมด! # 💀 Uses ~1 GB RAM to store the entire list! for line in all_lines: if 'ERROR' in line: print(line) break # 😱 แค่หา 1 บรรทัดแรก แต่โหลดทั้งไฟล์! # 😱 Only need first match, but loaded ENTIRE file! # สถานการณ์ที่ 2: การคัดลอก list # Scenario 2: List copying filtered_lines = [line for line in all_lines if 'ERROR' in line] # 💀 ตอนนี้ใช้แรม ~2 GB! (all_lines + filtered_lines) # 💀 Now using ~2 GB RAM! (all_lines + filtered_lines) processed_lines = [line.upper() for line in filtered_lines] # 💀 ตอนนี้ใช้แรม ~3 GB! (all_lines + filtered + processed) # 💀 Now using ~3 GB RAM! (all + filtered + processed)

💀 ปัญหาร้ายแรงของ Normal Function

1️⃣ Memory Explosion (หน่วยความจำระเบิด)

ตัวอย่างจริง: ไฟล์ log ขนาด 10 ล้านบรรทัด (1 GB)

Real example: A 10 million line log file (1 GB)

  • file.readlines() โหลดทั้งหมด = 1 GB RAM
  • ถ้าคุณคัดลอก list = อีก +1 GB RAM
  • ถ้าคุณทำ list comprehension = อีก +1 GB RAM
  • ผลลัพธ์: ใช้แรม 3 GB สำหรับงานที่ควรใช้แค่ไม่กี่ MB!

2️⃣ List Copying Problem (ปัญหาการคัดลอก List)

ใน Python เมื่อคุณสร้าง list ใหม่จาก list เดิม มันจะคัดลอกข้อมูลไม่ใช่แค่อ้างอิง

In Python, when you create a new list from an existing list, it copies the data, not just references it.

# การคัดลอก list ทำให้แรมพุ่ง! original = read_all_lines('big_file.txt') # 1 GB copy1 = [line for line in original] # +1 GB = 2 GB total copy2 = [line.strip() for line in copy1] # +1 GB = 3 GB total copy3 = [line.lower() for line in copy2] # +1 GB = 4 GB total!

3️⃣ Waiting Time (เวลารอที่สูญเปล่า)

  • ต้องรอให้โหลดทั้งไฟล์เสร็จก่อนถึงจะเริ่มประมวลผล
  • Must wait for entire file to load before processing can begin
  • ถ้าคุณต้องการแค่ 10 บรรทัดแรก ก็ต้องรอโหลด 10 ล้านบรรทัดเสร็จ!
  • If you only need first 10 lines, still must wait for all 10 million to load!

4️⃣ Memory Error (โปรแกรมล่ม)

# ถ้าไฟล์ใหญ่เกินไป... try: lines = read_all_lines('massive_10gb_file.txt') except MemoryError: print("💀 MemoryError: ไม่มีแรมพอ! (Not enough RAM!)") # โปรแกรมล่ม! ทำงานไม่ได้เลย! # Program crashes! Can't work at all!

🟢 พลังของ Generator (The Power of Generators)

# ✅ Generator Function - ประมวลผลทีละชิ้น แบบฉลาด! # ✅ Generator Function - Processes one piece at a time, smartly! def read_lines_generator(filename): with open(filename, 'r') as file: for line in file: yield line # ✨ คืนค่าทีละบรรทัด ไม่เก็บทั้งหมด! # ✨ Yields one line, doesn't store all! # สถานการณ์ที่ 1: ไฟล์ขนาด 1 GB # Scenario 1: 1 GB file for line in read_lines_generator('log_file_1gb.txt'): # 😊 ใช้แรมเพียง ~100 bytes ต่อบรรทัด! # 😊 Uses only ~100 bytes per line! if 'ERROR' in line: print(line) break # ✨ หยุดทันที! ไม่อ่านบรรทัดที่เหลือ! # ✨ Stops immediately! Doesn't read remaining lines! # สถานการณ์ที่ 2: Chaining ไม่มีการคัดลอก! # Scenario 2: Chaining without copying! def filter_errors(lines): for line in lines: if 'ERROR' in line: yield line # ไม่คัดลอก! แค่ส่งต่อ! def to_uppercase(lines): for line in lines: yield line.upper() # ไม่คัดลอก! แค่แปลงทีละบรรทัด! # 😊 ใช้แรมแค่ ~100 bytes ตลอด ไม่ว่าไฟล์จะใหญ่แค่ไหน! # 😊 Uses only ~100 bytes throughout, regardless of file size! for line in to_uppercase(filter_errors(read_lines_generator('huge_file.txt'))): print(line)

✨ ทำไม Generator ถึงทรงพลังกว่า

1️⃣ Constant Memory Usage (ใช้แรมคงที่)

Normal Function: แรมที่ใช้ = ขนาดของไฟล์ทั้งหมด
Generator: แรมที่ใช้ = ขนาดของ 1 บรรทัด (ประมาณ 100 bytes)

Normal Function: Memory used = entire file size
Generator: Memory used = size of 1 line (~100 bytes)

File Size Normal Function RAM Generator RAM
1 MB ~1 MB ~100 bytes
100 MB ~100 MB ~100 bytes
1 GB ~1 GB 💀 ~100 bytes ✨
10 GB MemoryError 💀💀💀 ~100 bytes ✨✨✨

2️⃣ No List Copying (ไม่มีการคัดลอก List)

Generator ไม่คัดลอกข้อมูล มันแค่ "ส่งต่อ" ข้อมูลไปยัง generator ถัดไป

Generators don't copy data, they just "pass along" data to the next generator.

# Generator Pipeline - ไม่คัดลอก! gen1 = read_lines_generator('big.txt') # ~100 bytes gen2 = filter_errors(gen1) # ~100 bytes (same line!) gen3 = to_uppercase(gen2) # ~100 bytes (same line!) # Total RAM: ~100 bytes ตลอด! # Total RAM: ~100 bytes throughout!

3️⃣ Lazy Evaluation (ประมวลผลแบบขี้เกียจ)

  • Normal Function: ต้องอ่านทั้งไฟล์เสร็จก่อน ถึงจะเริ่มทำงาน
  • Generator: เริ่มทำงานทันที อ่านเฉพาะเมื่อจำเป็น
# ถ้าต้องการ 5 บรรทัดแรกที่มี 'ERROR' # ❌ Normal Function: อ่านทั้งไฟล์ 10 ล้านบรรทัด! all_lines = read_all_lines('huge.txt') # รอ 30 วินาที... count = 0 for line in all_lines: if 'ERROR' in line: print(line) count += 1 if count >= 5: break # หา 5 บรรทัดแล้ว แต่อ่านไปแล้ว 10 ล้านบรรทัด! # ✅ Generator: อ่านเฉพาะที่จำเป็น! count = 0 for line in read_lines_generator('huge.txt'): # เริ่มทันที! if 'ERROR' in line: print(line) count += 1 if count >= 5: break # อ่านแค่ ~1000 บรรทัดเท่านั้น! (ถ้าเจอ ERROR ทุก 200 บรรทัด)

4️⃣ Infinite Sequences (ลำดับไม่สิ้นสุด)

Generator สามารถสร้างข้อมูลไม่สิ้นสุดได้ ซึ่ง Normal Function ทำไม่ได้เลย!

Generators can create infinite sequences, which Normal Functions simply cannot do!

# ❌ Normal Function: ไม่สามารถทำได้! def infinite_numbers(): numbers = [] i = 0 while True: numbers.append(i) # 💀 จะกินแรมไปเรื่อยๆ จนโปรแกรมล่ม! i += 1 return numbers # ไม่มีวันถึงบรรทัดนี้! # ✅ Generator: ทำได้ง่ายๆ! def infinite_numbers_gen(): i = 0 while True: yield i # ✨ ใช้แรมแค่ int เดียว! i += 1 # ใช้งานได้ปกติ! for num in infinite_numbers_gen(): print(num) if num >= 100: break # หยุดเมื่อต้องการ

5️⃣ Pipeline Composition (ประกอบ Pipeline ได้)

Generator สามารถเชื่อมต่อกันได้โดยไม่ต้องคัดลอกข้อมูล ทำให้สร้าง pipeline ที่ซับซ้อนได้ง่าย

Generators can be chained without copying data, making it easy to build complex pipelines.

📊 สรุปเปรียบเทียบ (Summary Comparison)

คุณสมบัติ (Feature) Normal Function Generator
Memory Usage ❌ ใช้เท่ากับขนาดไฟล์ ✅ ใช้แค่ 1 item
List Copying ❌ คัดลอกทุกครั้ง ✅ ไม่คัดลอก
Start Time ❌ รอโหลดทั้งไฟล์ ✅ เริ่มทันที
Large Files ❌ MemoryError ✅ ทำงานได้
Infinite Data ❌ ทำไม่ได้ ✅ ทำได้
Pipeline ❌ ยาก แรมพุ่ง ✅ ง่าย ไม่กินแรม

✅ ทำไม Generator ดีกว่า? (Why Are Generators Better?)

  1. Memory Efficient (ประหยัดหน่วยความจำ)
    ไม่โหลดข้อมูลทั้งหมดเข้า RAM พร้อมกัน - เหมาะสำหรับไฟล์ขนาดใหญ่
    Doesn't load all data into RAM at once - perfect for large files
  2. Lazy Evaluation (ประมวลผลแบบขี้เกียจ)
    ประมวลผลเมื่อต้องการใช้จริง ไม่เสียเวลาประมวลผลข้อมูลที่ไม่ได้ใช้
    Processes only when needed - doesn't waste time on unused data
  3. Pipeline Processing (ประมวลผลแบบสายพาน)
    เชื่อมต่อ generator หลายตัวเข้าด้วยกันได้ เช่น อ่าน → กรอง → แปลง
    Can chain multiple generators together - e.g., read → filter → transform
  4. Infinite Sequences (ลำดับไม่มีที่สิ้นสุด)
    สามารถสร้างข้อมูลไม่สิ้นสุดได้ เช่น stream ข้อมูล real-time
    Can create infinite sequences - e.g., real-time data streams
  5. Better Performance (ประสิทธิภาพดีกว่า)
    เริ่มส่งผลลัพธ์ได้ทันที ไม่ต้องรอประมวลผลทั้งหมดเสร็จก่อน
    Starts yielding results immediately - no need to wait for all processing

พลังของ Generator Chaining

Generator สามารถเชื่อมต่อกันเป็นลูกโซ่ได้ (Chaining) ทำให้เราสามารถสร้าง data pipeline ที่ประมวลผลข้อมูลทีละขั้นตอนได้อย่างมีประสิทธิภาพ แต่ละ generator ทำหน้าที่เพียงอย่างเดียว ทำให้โค้ดอ่านง่าย บำรุงรักษาง่าย และทดสอบง่าย

Generators can be chained together, allowing us to create efficient data pipelines that process data step by step. Each generator has a single responsibility, making code readable, maintainable, and testable.

🔗 ตัวอย่าง: Generator Pipeline แบบสมบูรณ์

เราจะสร้าง pipeline ที่มี 3 ขั้นตอน: อ่าน → กรอง → แปลง

We'll create a 3-stage pipeline: Read → Filter → Transform

# generator_pipeline.py # สาธิตการเชื่อมต่อ generators เข้าด้วยกัน # Demonstrating generator chaining class GeneratorPipeline: """ คลาสที่แสดงการใช้ generator chaining Class demonstrating generator chaining """ def __init__(self, filename): self.filename = filename self.stats = { 'lines_read': 0, 'lines_filtered': 0, 'lines_transformed': 0 } # ======================================== # ขั้นที่ 1: READER GENERATOR # Step 1: READER GENERATOR # ======================================== def read_lines(self): """ Generator ที่อ่านไฟล์ทีละบรรทัด Generator that reads file line by line Yields: str: บรรทัดจากไฟล์ (line from file) """ try: with open(self.filename, 'r', encoding='utf-8') as file: for line in file: self.stats['lines_read'] += 1 yield line.strip() except FileNotFoundError: print(f"ไม่พบไฟล์: {self.filename}") # ======================================== # ขั้นที่ 2: FILTER GENERATORS # Step 2: FILTER GENERATORS # ======================================== def filter_non_empty(self, lines): """ กรองเฉพาะบรรทัดที่ไม่ว่าง Filter out empty lines Parameters: lines: generator ของบรรทัด (generator of lines) Yields: str: บรรทัดที่ไม่ว่าง (non-empty line) """ for line in lines: if line: # ถ้าบรรทัดไม่ว่าง (if line is not empty) yield line def filter_by_keyword(self, lines, keyword): """ กรองเฉพาะบรรทัดที่มีคำที่ระบุ Filter lines containing specific keyword Parameters: lines: generator ของบรรทัด (generator of lines) keyword (str): คำที่ต้องการค้นหา (keyword to search) Yields: str: บรรทัดที่มีคำที่ระบุ (line containing keyword) """ for line in lines: if keyword.lower() in line.lower(): self.stats['lines_filtered'] += 1 yield line def filter_by_length(self, lines, min_length=10): """ กรองเฉพาะบรรทัดที่มีความยาวขั้นต่ำ Filter lines with minimum length Parameters: lines: generator ของบรรทัด (generator of lines) min_length (int): ความยาวขั้นต่ำ (minimum length) Yields: str: บรรทัดที่ยาวพอ (line with sufficient length) """ for line in lines: if len(line) >= min_length: yield line # ======================================== # ขั้นที่ 3: TRANSFORM GENERATORS # Step 3: TRANSFORM GENERATORS # ======================================== def to_uppercase(self, lines): """ แปลงเป็นตัวพิมพ์ใหญ่ Transform to uppercase Parameters: lines: generator ของบรรทัด (generator of lines) Yields: str: บรรทัดที่เป็นตัวพิมพ์ใหญ่ (uppercase line) """ for line in lines: yield line.upper() def add_line_numbers(self, lines): """ เพิ่มเลขบรรทัดข้างหน้า Add line numbers Parameters: lines: generator ของบรรทัด (generator of lines) Yields: str: บรรทัดพร้อมเลขหน้า (numbered line) """ for i, line in enumerate(lines, 1): yield f"{i}. {line}" def extract_words(self, lines): """ แปลงบรรทัดเป็น dictionary ที่มีข้อมูลเชิงลึก Transform lines into detailed dictionary Parameters: lines: generator ของบรรทัด (generator of lines) Yields: dict: ข้อมูลบรรทัดแบบละเอียด (detailed line info) """ for line in lines: words = line.split() self.stats['lines_transformed'] += 1 yield { 'text': line, 'word_count': len(words), 'char_count': len(line), 'words': words, 'first_word': words[0] if words else '' } def highlight_keyword(self, lines, keyword): """ เน้นคำที่ค้นหาด้วย *** *** Highlight keyword with *** *** Parameters: lines: generator ของบรรทัด (generator of lines) keyword (str): คำที่ต้องการเน้น (keyword to highlight) Yields: str: บรรทัดที่มีการเน้นคำ (line with highlighted keyword) """ for line in lines: highlighted = line.replace( keyword, f"***{keyword}***" ) yield highlighted # ======================================== # ตัวอย่างการใช้งาน: CHAINING GENERATORS # Usage Examples: CHAINING GENERATORS # ======================================== if __name__ == "__main__": # สร้างไฟล์ตัวอย่างสำหรับทดสอบ # Create sample file for testing with open('sample_data.txt', 'w', encoding='utf-8') as f: f.write("""Python is a powerful programming language It is easy to learn and use Python supports object-oriented programming You can create generators in Python Generators are memory efficient They use yield instead of return Python generators are amazing tools""") pipeline = GeneratorPipeline('sample_data.txt') print("=" * 80) print("ตัวอย่างที่ 1: อ่าน → กรองคำว่า 'Python' → เพิ่มเลขบรรทัด") print("Example 1: Read → Filter 'Python' → Add line numbers") print("=" * 80) # 🔗 Chain: read → filter by keyword → add line numbers result1 = pipeline.add_line_numbers( pipeline.filter_by_keyword( pipeline.read_lines(), 'Python' ) ) for line in result1: print(line) print(f"\n📊 สถิติ: {pipeline.stats}\n") # รีเซ็ตสถิติ (Reset stats) pipeline.stats = {'lines_read': 0, 'lines_filtered': 0, 'lines_transformed': 0} print("=" * 80) print("ตัวอย่างที่ 2: อ่าน → กรองบรรทัดยาว → เน้นคำ → ตัวพิมพ์ใหญ่") print("Example 2: Read → Filter long lines → Highlight word → Uppercase") print("=" * 80) # 🔗 Chain: read → filter empty → filter length → highlight → uppercase result2 = pipeline.to_uppercase( pipeline.highlight_keyword( pipeline.filter_by_length( pipeline.filter_non_empty( pipeline.read_lines() ), min_length=30 ), 'generator' ) ) for line in result2: print(line) print(f"\n📊 สถิติ: {pipeline.stats}\n") # รีเซ็ตสถิติ (Reset stats) pipeline.stats = {'lines_read': 0, 'lines_filtered': 0, 'lines_transformed': 0} print("=" * 80) print("ตัวอย่างที่ 3: อ่าน → กรองคำว่า 'Python' → แปลงเป็น dictionary") print("Example 3: Read → Filter 'Python' → Transform to dictionary") print("=" * 80) # 🔗 Chain: read → filter → extract detailed info result3 = pipeline.extract_words( pipeline.filter_by_keyword( pipeline.read_lines(), 'Python' ) ) for data in result3: print(f"📝 Text: {data['text']}") print(f" 📊 Words: {data['word_count']}, Chars: {data['char_count']}, First: '{data['first_word']}'") print() print(f"📊 สถิติสุดท้าย (Final stats): {pipeline.stats}")

🎯 ผลลัพธ์ที่คาดหวัง (Expected Output)

================================================================================
ตัวอย่างที่ 1: อ่าน → กรองคำว่า 'Python' → เพิ่มเลขบรรทัด
Example 1: Read → Filter 'Python' → Add line numbers
================================================================================
1. Python is a powerful programming language
2. Python supports object-oriented programming
3. You can create generators in Python
4. Python generators are amazing tools

📊 สถิติ: {'lines_read': 7, 'lines_filtered': 4, 'lines_transformed': 0}

================================================================================
ตัวอย่างที่ 2: อ่าน → กรองบรรทัดยาว → เน้นคำ → ตัวพิมพ์ใหญ่
Example 2: Read → Filter long lines → Highlight word → Uppercase
================================================================================
PYTHON IS A POWERFUL PROGRAMMING LANGUAGE
PYTHON SUPPORTS OBJECT-ORIENTED PROGRAMMING
YOU CAN CREATE ***GENERATOR***S IN PYTHON
PYTHON ***GENERATOR***S ARE AMAZING TOOLS

📊 สถิติ: {'lines_read': 7, 'lines_filtered': 0, 'lines_transformed': 0}

================================================================================
ตัวอย่างที่ 3: อ่าน → กรองคำว่า 'Python' → แปลงเป็น dictionary
Example 3: Read → Filter 'Python' → Transform to dictionary
================================================================================
📝 Text: Python is a powerful programming language
   📊 Words: 6, Chars: 42, First: 'Python'

📝 Text: Python supports object-oriented programming
   📊 Words: 4, Chars: 42, First: 'Python'

📝 Text: You can create generators in Python
   📊 Words: 6, Chars: 35, First: 'You'

📝 Text: Python generators are amazing tools
   📊 Words: 5, Chars: 34, First: 'Python'

📊 สถิติสุดท้าย (Final stats): {'lines_read': 7, 'lines_filtered': 4, 'lines_transformed': 4}

💡 ทำไม Chaining ถึงทรงพลัง? (Why is Chaining Powerful?)

  1. Single Responsibility (หน้าที่เดียว)
    แต่ละ generator ทำแค่สิ่งเดียว ทำให้ง่ายต่อการเข้าใจและทดสอบ
    Each generator does one thing well, making it easy to understand and test
  2. Composability (ประกอบกันได้)
    สามารถนำ generators มาผสมกันได้หลายแบบ ไม่ต้องเขียนโค้ดใหม่
    Can mix and match generators in different ways without rewriting code
  3. Memory Efficient (ประหยัดหน่วยความจำ)
    ประมวลผลทีละชิ้น ไม่โหลดข้อมูลทั้งหมดเข้า memory
    Processes one item at a time, doesn't load all data into memory
  4. Lazy Execution (ทำงานเมื่อจำเป็น)
    ถ้าเรา break ออกจาก loop เร็ว ก็ไม่ต้องประมวลผลข้อมูลที่เหลือ
    If you break early from the loop, remaining data isn't processed
  5. Easy to Debug (ดีบักง่าย)
    สามารถทดสอบแต่ละขั้นตอนแยกกันได้
    Can test each stage independently
# 💡 เทคนิคการเขียน: สร้าง helper function สำหรับ chaining # Writing Technique: Create helper function for chaining def create_pipeline(filename, keyword, min_length=0): """ สร้าง pipeline ที่ใช้งานบ่อยๆ ให้สั้นลง Create commonly-used pipeline in shorter form """ pipeline = GeneratorPipeline(filename) # เชื่อมต่อ generators เข้าด้วยกัน # Chain generators together result = pipeline.read_lines() result = pipeline.filter_non_empty(result) if min_length > 0: result = pipeline.filter_by_length(result, min_length) if keyword: result = pipeline.filter_by_keyword(result, keyword) result = pipeline.extract_words(result) return result # ใช้งาน (Usage) for data in create_pipeline('sample_data.txt', 'Python', min_length=30): print(f"{data['text']} - {data['word_count']} words")

ตัวอย่างการใช้งานจริง (Real-World Use Cases)

ตัวอย่างการประยุกต์ใช้ Generator Pipeline ในงานจริง

Real-world applications of Generator Pipeline

# ตัวอย่าง: Log File Analysis # วิเคราะห์ไฟล์ log ขนาดใหญ่หา error messages # Analyze large log files for error messages class LogAnalyzer(GeneratorPipeline): def filter_errors(self, lines): """กรองเฉพาะบรรทัดที่มี ERROR""" for line in lines: if 'ERROR' in line or 'CRITICAL' in line: yield line def parse_log_entry(self, lines): """แปลง log entry เป็น structured data""" import re for line in lines: # สมมติว่า format: [2024-01-01 10:30:45] ERROR: message match = re.match(r'\[(.+?)\] (\w+): (.+)', line) if match: yield { 'timestamp': match.group(1), 'level': match.group(2), 'message': match.group(3) } # ใช้งาน: อ่าน → กรอง errors → แปลง → วิเคราะห์ # Usage: read → filter errors → parse → analyze analyzer = LogAnalyzer('app.log') errors = analyzer.parse_log_entry( analyzer.filter_errors( analyzer.read_lines() ) ) error_count = {} for entry in errors: msg = entry['message'] error_count[msg] = error_count.get(msg, 0) + 1 print("Top Errors:") for error, count in sorted(error_count.items(), key=lambda x: x[1], reverse=True)[:5]: print(f" {count}x: {error}")

สรุป (Summary)

🎯 เส้นทางการพัฒนา (Development Path)

  1. Simple Script - เริ่มต้นง่ายๆ แต่ไม่ยืดหยุ่น
  2. + Entry Point - สามารถ import ได้
  3. + Function - ใช้ซ้ำได้ มี error handling
  4. + Class - จัดระเบียบดี เก็บ state ได้
  5. + Generator - ประหยัด memory รองรับไฟล์ใหญ่

💡 ข้อแนะนำสำหรับการเขียนโค้ดที่ดี (Best Practices)

  • เริ่มต้นจากง่ายไปซับซ้อน - จัดระเบียบเมื่อจำเป็น
    Start simple, organize when necessary
  • ใช้ if __name__ == "__main__": เสมอในไฟล์ที่ต้องการ import
    Always use entry point in importable files
  • เขียน docstring อธิบายฟังก์ชันและ class
    Write docstrings to explain functions and classes
  • ใช้ generator เมื่อต้องการจัดการข้อมูลจำนวนมาก
    Use generators for handling large amounts of data
  • จัดการข้อผิดพลาดด้วย try-except
    Handle errors with try-except blocks
  • ตั้งชื่อตัวแปรและฟังก์ชันให้สื่อความหมาย
    Use meaningful variable and function names

⚠️ สิ่งที่ควรหลีกเลี่ยง (Common Mistakes to Avoid)

  • ไม่ใช้ entry point ทำให้ import แล้วโค้ดรันทันที
    Not using entry point causes code to run on import
  • โหลดไฟล์ใหญ่ทั้งหมดเข้า memory
    Loading large files entirely into memory
  • ไม่จัดการข้อผิดพลาด (error handling)
    Not handling errors properly
  • hardcode ค่าต่างๆ แทนที่จะใช้ parameter
    Hardcoding values instead of using parameters
  • ไม่เขียน docstring หรือ comment
    Not writing docstrings or comments