วันอาทิตย์ที่ 6 เมษายน พ.ศ. 2568

Python PEP 8 คืออะไร

    ขอเกริ่นนำสักนิดนึง 

        blog นี้ก็เขียนมาไว้เหมือนเป็น note ของตัวเองไว้กลับมาอ่านแหละ แต่ก็แชร์ให้คนอื่นอ่านด้วย เผื่อมีประโยชน์บ้างเล็กๆน้อยๆ ภาษาไม่ทางการนะครับ ก๊อบมาบ้างอธิบายเพิ่มเองบ้าง

    เรื่องมีอยู่ว่า คือทำงานสาย IT มาสักพัก แล้วได้มีโอกาสมาเขียนโปรแกรม โดยที่ office ก็ใช้ python กัน เนื่องจากเรียนรู้ได้เร็ว เขียนง่าย lib เยอะ แต่ปัญหาคือ เขียนแล้วเจอ bug บ่อย แล้วก็เวลากลับมาแก้ มันช่างลำบากซะเหลือเกิน เลยไปหาข้อมูลมาว่าทำยังไงถึงจะ เขียนดีขึ้น คือ bug น้อยลง เขียนได้เร็วขึ้น แล้วหัวข้อแรกที่โดนแนะนำมาคือเขียนให้มันอ่านง่ายก่อน โดยให้ไปศึกษา PEP8 คืออะไร

เข้าสู่เนื้อหา

PEP 8 คืออะไร และถ้าทำตามแล้วช่วยอะไร

    PEP 8 (Python Enhancement Proposal 8) คือแนวทางปฏิบัติที่ช่วยให้โค้ด Python อ่านง่าย เป็นมาตรฐานเดียวกัน และเข้าใจได้ง่ายขึ้นในทีมพัฒนา โดยเฉพาะโค้ดที่ใช้ในโครงการขนาดใหญ่หรือโอเพ่นซอร์ส

    แปลง่ายๆคือข้อแนะนำในการเขียนให้ดูสวย อ่านง่ายนั่นแหละ เวลากลับมาแก้หรือเพื่อนมาอ่านจะได้เข้าใจง่ายๆ ไม่งง ไม่นั่งกุมขมับ (ผมเคยโดนมาแล้ว เลือกเขียนใหม่ ดีกว่าแก้อะไรก็ไม่รู้ ที่อยู่ตรงหน้านี้)
  • ช่วยให้โค้ดอ่านง่ายและลดความซับซ้อน

  • ลดปัญหาเกี่ยวกับโค้ดที่อ่านยากหรือโค้ดที่แต่ละคนเขียนไม่เหมือนกัน

  • มีผลต่อการ review โค้ด และช่วยลด bug ได้

หัวข้อหลักๆที่อ่านมาก็มี ประมาณนี้

1. การตั้งชื่อ ของทั้งตัวแปร, Class และ Function
2. Indent หรือย่อหน้า 
3. Space หรือ การเว้นวรรค
4. Line Length & Line Breaks (ความยาวบรรทัดและการขึ้นบรรทัดใหม่)
5. หลักการจัด import
6. ไม่ควรใช้หลายคำสั่งในบันทัดเดียว
7. Comments & Docstrings (การเขียนคอมเมนต์และคำอธิบายฟังก์ชัน)

คือมันไม่ได้มี 8 ข้อตามชื่อนะ เหมือนตัวเลขนี้เป็นลำดับเอกสารมากกว่า เพราะหลังๆมามีมาตรฐาน pep ไป 733 แล้ว ใน python 3.13

ตอนอ่านครั้งแรก จะหาให้มันครบ 8 ข้อให้ได้

ตัวอย่าง PEP ที่เคยผ่านตามาก็ 
PEP 695 type parameter syntax, 
PEP 728 รองรับ strict mode, 
PEP 734 สำหรับ embedded use

มาดูตัวอย่างกัน

1. การตั้งชื่อ ของทั้งตัวแปร, Class และ Function
  • ตัวแปรและฟังก์ชัน: ใช้ snake_case (ตัวอักษรเล็กทั้งหมด คั่นด้วย _)

    total_price = 100 def calculate_discount(price): return price * 0.1
  • คลาส: ใช้ CamelCase (ตัวอักษรตัวแรกของแต่ละคำเป็นตัวใหญ่)

    class ShoppingCart: pass
  • ค่าคงที่ (Constants): ใช้ UPPER_CASE

    MAX_RETRIES = 5
  • ตัวแปรที่ไม่ใช้ (throwaway variables): ใช้
    (ที่ปกติใช้ i กันนั่นแหละ และผมก็ยังใช้ i อยูบ้าง)

    for _ in range(5): print("Hello")
2. Indent หรือย่อหน้า ให้ใช้ 4 spaces ต่อการย่อหน้า 1 ระดับ ไม่ควรใช้ tab
    คนที่เขียน python จะรู้ว่าเราใช้ indent ในการบอกระดับของ code แทน {...} 
ปกติผมก็กด tab แหละ แต่จะมีปัญหาตอนที่เราไปแก้ใน vi 
ถ้าใน vs code ก็ไม่มีประเด็นอะไร 
เดี๋ยวลองเปลี่ยนดู น่าจะลดปัญหา error indent ได้

3. Space หรือ การเว้นวรรค อันนี้ค่อนข้างวุ่นวายหน่อย
    ใน PEP 8 มีการแนะนำเกี่ยวกับการใช้เว้นวรรคด้วย อันนี้ก็เพื่อความสวยงามแหละ แล้วก็น่าจะทำให้ code อ่านง่ายขึ้นด้วย ทั้งหมดจะเว้นวรรคแค่ทีเดียวนะ มีหลักประมาณนี้

  • เว้นวรรคหลัง comma เพื่อให้อ่านง่าย แต่ด้านหน้าไม่ต้องเว้นนะ
    spam(ham[1], {eggs: 2})
    
  • เว้นวรรคทั้งหน้าและหลังของ ตัวดำเนินการแบบ binary  (เช่น =, +=, ==, and, or)

    ซึ่งรวมถึง:

    • การกำหนดค่า (=)

    • การกำหนดค่าร่วม (+=, -=, ฯลฯ)

    • การเปรียบเทียบ (==, <, >, !=, <=, >=, in, not in, is, is not)

    • การดำเนินการแบบตรรกะ (and, or, not)

    x = 1
    y = 2
    long_variable = 3
    
    if foo == 'blah':
  • หากมีหลาย operator ที่มีลำดับความสำคัญต่างกัน ควรเว้นวรรคกับตัวที่มีความสำคัญต่ำสุด เพื่อให้โค้ดอ่านง่าย
    i = i + 1
    submitted += 1
    x = x*2 - 1
    hypot2 = x*x + y*y
    c = (a+b) * (a-b)
  • แต่ไม่เว้นวรรคทั้งหน้าและหลังของ = ใน default arguments และ keyword arguments
    ตรงนี้หมายถึงตอนประกาศ function จะมีการส่งค่าเข้ามา และกำหนดค่า default ได้
    def complex(real, imag=0.0):
        return magic(r=real, i=imag)
  • ไม่เว้นวรรคเป็นตัวสุดท้ายตอนจบบันทัด

  • ไม่เว้นวรรคทั้งหน้าและหลังวงเล็บ ไม่ว่าจะเป็น ( ) { } [ ] ให้เขียนติดกันไปเลย
    spam(ham[1], {eggs: 2})
  • สำหรับ colon : อันนี้จะเรื่องมากหน่อย โดยปกติไม่ควรเว้นวรรคทั้งหน้าและหลัง แต่ถ้ามีการคำนวนอยู่ด้วย ก็สามารถเว้นได้โดยใช้หลักการ เว้นวรรคกับตัวที่มีความสำคัญต่ำสุด และ โคลอนถือเป็นตัวดำเนินการที่มีลำดับความสำคัญต่ำที่สุด
    ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
    ham[lower:upper], ham[lower:upper:], ham[lower::step]
    ham[lower+offset : upper+offset]
    ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
    ham[lower + offset : upper + offset]
4. Line Length & Line Breaks (ความยาวบรรทัดและการขึ้นบรรทัดใหม่)
  • โดยปกติไม่ควรมีความยาวเกิน 79 ตัวอักษร ในแต่ละบันทัด
  • ใช้ \ หรือ () เพื่อขึ้นบรรทัดใหม่หากต้องการให้โค้ดยาว ๆ อ่านง่ายขึ้น
  • หากมีการคำนวนยาวๆแล้วอยากขึ้นบันทัดใหม่ ให้เอาเครื่องหมายลงมาด้วย
    income = (gross_wages
              + taxable_interest
              + (dividends - qualified_dividends)
              - ira_deduction
              - student_loan_interest)
  • ควรเว้น 2 บรรทัด ระหว่าง คลาส หรือ ฟังก์ชัน ที่ไม่ได้อยู่ใน Class
  • ภายในคลาส ควรเว้น 1 บรรทัด ระหว่าง Methods (Function ภายใน Class) เพื่อแยกการทำงานของแต่ละ Methods 
  • หากภายในคลาสมีตัวแปรระดับคลาส (class attributes) ให้เว้น 1 บรรทัด ระหว่างตัวแปรกับ Methods แรก
  • สามารถเว้น 1 บรรทัด เพื่อแยก กลุ่มของการทำงาน (Logical Sections) ภายในฟังก์ชันได้

  • หากต้องการ แยกกลุ่มคำสั่งที่มีความเกี่ยวข้องกัน สามารถเว้นบรรทัดเพื่อให้โค้ดอ่านง่ายขึ้น
        Note : เอาจริงๆหัวข้อนี้หลายอันหาไม่เจอในเอกสาร แต่ GPT บอกมา แล้วพอทำตามแล้วคิดว่ามันสวยดีเลยใส่ไว้ด้วย

5. การ Import ควรเรียงลำดับ import ให้เป็นระเบียบ 
  • Standard library (ไลบรารีที่ติดมากับ Python)
  • Third-party libraries (ไลบรารีที่ติดตั้งเพิ่มเติม)
  • Local application imports (ไลบรารีที่เราเขียนเอง)
  • หลีกเลี่ยง from module import * เพราะอาจทำให้เกิดปัญหาชื่อซ้ำกัน
ตัวอย่างก็ประมาณนี้
import os
import sys

import numpy as np
import requests

from my_project.utils import helper_function



6. ไม่ควรใช้หลายคำสั่งในบันทัดเดียว

ตัวอย่างที่ผิด (เขียนมาตั้งนานถึ่งรู้ว่า python ใช้ ; ในการแบ่งคำสั่งได้)
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()

ตัวอย่างที่ถูก
if foo == 'blah':
    do_blah_thing()
do_one()
do_two()
do_three()

7. Comments & Docstrings (การเขียนคอมเมนต์และคำอธิบายฟังก์ชัน)
  • ควรใช้ภาษาอังกฤษในการเขียน comment
  • ใช้ # และเว้นวรรคก่อนเขียนข้อความ สำหรับ comment
  • ใช้ """ """ สำหรับ docstring 

แถมท้ายนิดนึง เป็นเทคนิกเล็กๆน้อยๆที่แนะนำ

    นอกจากที่กล่าวมาแล้ว PEP 8 ยังมีแนวทางสำคัญอื่น ๆ ที่ช่วยให้โค้ด Python เป็นระเบียบ อ่านง่าย และปฏิบัติตามมาตรฐานที่ดี โดยหัวข้อเพิ่มเติมที่ควรเรียนรู้ ได้แก่:


1️⃣ การใช้ Boolean Values อย่างเหมาะสม

  • ใช้ if variable: แทน if variable == True:

  • ใช้ if not variable: แทน if variable == False:

ตัวอย่างที่แนะนำ


if is_valid: print("Valid") if not is_empty: print("Not empty")

ตัวอย่างที่ควรหลักเลี่ยง


if is_valid == True: # ❌ ไม่จำเป็นต้องเปรียบเทียบกับ True print("Valid") if is_empty == False: # ❌ ไม่จำเป็นต้องเปรียบเทียบกับ False print("Not empty")

2️⃣ การเปรียบเทียบค่ากับ None

  • ควรใช้ is และ is not เมื่อเปรียบเทียบกับ None

  • ห้ามใช้ == หรือ != เพราะอาจเกิดปัญหาในบางกรณี

ตัวอย่างที่แนะนำ


if my_var is None: print("Variable is None") if my_var is not None: print("Variable is not None")

ตัวอย่างที่ควรหลักเลี่ยง


if my_var == None: # ❌ ควรใช้ 'is' แทน print("Variable is None")

3️⃣ การใช้ with แทน try-finally ในการจัดการไฟล์

  • ใช้ with statement เพื่อเปิดไฟล์หรือจัดการ resource ต่าง ๆ แทน try-finally

  • with จะช่วยปิดไฟล์อัตโนมัติ แม้ว่าจะเกิดข้อผิดพลาดระหว่างการอ่านไฟล์

ตัวอย่างที่แนะนำ


with open("data.txt", "r") as file: content = file.read()

ตัวอย่างที่ผิด


file = open("data.txt", "r") try: content = file.read() finally: file.close() # ❌ ต้องปิดไฟล์เอง

4️⃣ การใช้ตัวดำเนินการแบบ in เมื่อเช็คค่าสมาชิก

  • ควรใช้ if item in list แทน if item == element1 or item == element2

  • อ่านง่ายขึ้น และมีประสิทธิภาพมากกว่า

ตัวอย่างที่แนะนำ


if fruit in ["apple", "banana", "cherry"]: print("This is a fruit")

ตัวอย่างที่ควรหลักเลี่ยง


if fruit == "apple" or fruit == "banana" or fruit == "cherry": print("This is a fruit") # ❌ โค้ดยาวเกินไป

5️⃣ หลีกเลี่ยงการใช้ Mutable Default Arguments

  • หลีกเลี่ยงการใช้ list, dict, หรือ set เป็น default argument เพราะค่าจะถูกแชร์ข้ามการเรียกใช้ฟังก์ชัน

ตัวอย่างที่แนะนำ


def add_item(item, item_list=None): if item_list is None: item_list = [] item_list.append(item) return item_list

ตัวอย่างที่ควรหลักเลี่ยง


def add_item(item, item_list=[]): # ❌ ค่า list ถูกแชร์ข้ามการเรียกใช้ฟังก์ชัน item_list.append(item) return item_list

6️⃣ การจัดการ Exception อย่างเหมาะสม

  • ควรใช้ try-except และจับข้อผิดพลาดให้เฉพาะเจาะจง ไม่ควรจับ Exception โดยไม่มีเหตุผล

  • หลีกเลี่ยง except: ที่จับข้อผิดพลาดทั้งหมดโดยไม่ระบุประเภทของข้อผิดพลาด

ตัวอย่างที่แนะนำ


try: value = int(user_input) except ValueError: print("Invalid input! Please enter a number.")

ตัวอย่างที่ควรหลักเลี่ยง


try: value = int(user_input) except: # ❌ ไม่ระบุประเภทข้อผิดพลาด print("Something went wrong!")

7️⃣ การจัดการ Import Statement ให้มีประสิทธิภาพ

  • หลีกเลี่ยง import * เพราะทำให้โค้ดอ่านยากและอาจเกิดปัญหาการซ้อนทับของชื่อฟังก์ชัน

  • ใช้ from module import function_name เฉพาะเมื่อใช้บางฟังก์ชันของโมดูลนั้น

ตัวอย่างที่แนะนำ


import os from datetime import datetime

ตัวอย่างที่ควรหลักเลี่ยง


from math import * # ❌ ทำให้โค้ดอ่านยาก และอาจเกิดการซ้อนทับของฟังก์ชัน

8️⃣ หลีกเลี่ยงการใช้ Lambda ที่ซับซ้อนเกินไป

  • Lambda ควรใช้สำหรับฟังก์ชันสั้น ๆ เท่านั้น ถ้ามีเงื่อนไขซับซ้อนควรใช้ def แทน

  • คำอธิบายเรื่อง lambda อยู่อีกบทความนะครับ กดที่ lambda ที่หัวข้อได้เลย

ตัวอย่างที่แนะนำ


add = lambda x, y: x + y # ใช้ในกรณีง่าย ๆ

ตัวอย่างที่ควรหลักเลี่ยง


lambda_func = lambda x: "Positive" if x > 0 else "Negative" if x < 0 else "Zero" # ❌ ซับซ้อนเกินไป ควรใช้ def function แทน

เขียนใหม่ให้แนะนำ


def check_number(x): if x > 0: return "Positive" elif x < 0: return "Negative" else: return "Zero"

🔥 สรุปหัวข้อเพิ่มเติมที่ควรรู้ใน PEP 8

หัวข้อคำแนะนำที่สำคัญ
✅ ใช้ Boolean อย่างเหมาะสมใช้ if variable: แทน if variable == True:
✅ เปรียบเทียบ None ด้วย isใช้ if x is None: แทน if x == None:
✅ ใช้ with เปิดไฟล์ช่วยปิดไฟล์อัตโนมัติ
✅ ใช้ in แทน or หลายตัวif x in [a, b, c]:
✅ หลีกเลี่ยง Mutable Default Argumentsใช้ None แล้วสร้าง list หรือ dict ภายในฟังก์ชัน
✅ จับข้อผิดพลาดแบบเฉพาะเจาะจงใช้ except ValueError: แทน except:
✅ หลีกเลี่ยง import *ควร import เฉพาะสิ่งที่ใช้
✅ หลีกเลี่ยง Lambda ที่ซับซ้อนใช้ def แทน Lambda ที่มีเงื่อนไขซับซ้อน


ประมาณนี้ครับ ถ้าผมผิดตรงไหนบอกด้วยนะ เดี๋ยวมาแก้

ไม่มีความคิดเห็น:

แสดงความคิดเห็น