Micropython : Import ✅ เลือกรับกับสิ่งที่ต้อง

การพิมพ์ข้อความ “Hello, World!” บนจอภาพปรากฏครั้งแรกในหนังสือ “A Tutorial Introduction to the Language B” (1972) เขียนโดย Brian Kernighan ซึ่งเป็นตัวอย่างการแสดงข้อความง่าย ๆ ด้วยภาษา B ที่เป็นภาษาต้นแบบของภาษา C

เมื่อภาษาซีถือกำเนิดขึ้น ตัวอย่าง “Hello, World!” ถูกนำมาใช้ในหนังสือ “The C Programming Language” (1978) โดย Brian Kernighan และ Dennis Ritchie หนังสือเล่มนี้ใช้โปรแกรมนี้เป็นตัวอย่างแรกในการแนะนำภาษา C เพื่อให้ผู้อ่านเข้าใจการทำงานของโปรแกรมได้อย่างง่ายดายและเห็นผลลัพธ์ชัดเจน ดังตัวอย่างนี้:

#include <stdio.h>

int main() {
printf("Hello, World!\n");
}

โปรแกรมนี้ไม่เพียงแค่แสดงข้อความธรรมดา แต่ยังเป็นสัญลักษณ์ของจุดเริ่มต้นที่เรียบง่ายในโลกของการเขียนโปรแกรม และกลายเป็นธรรมเนียมที่ผู้เรียนภาษาโปรแกรมทั่วโลกมักเริ่มต้นโปรแกรมแรกของการเรียนรู้ภาษานั้นด้วยการพิมพ์ “Hello, World!” จนถึงทุกวันนี้!!!!

การพิมพ์ข้อความออกสู่จอภาพที่ดูเรียบง่าย ตรงไปตรงมา กระชับสั้น แต่ภายใต้คำสั่งสั้น ๆ เข้าใจง่ายนั้น กลับแฝงไว้ด้วยขั้นตอนที่ซับซ้อนเกินกว่าผู้มาใหม่จะจินตนาการได้ ขั้นตอนเช่น การส่งข้อมูลผ่านระบบปฏิบัติการ การจัดรูปแบบข้อความ และการทำงานร่วมกับอุปกรณ์แสดงผล โดยเฉพาะในระบบสมองกลฝังตัวที่ต้องเชื่อมต่อกับอุปกรณ์แสดงผล เช่น จอ LCD ผ่านโปรโตคอล I2C, SPI หรือ UART นั้น ล้วนต้องการความเข้าใจที่ลึกซึ้ง ความง่ายดายของการใช้งานที่ซ่อนขั้นตอนยุ่งยากเกิดขึ้นได้เพราะเครื่องมือที่ใช้ในการพัฒนาโปรแกรมภาษานั้น ๆ ได้จัดเตรียมกระบวนการเหล่านี้ไว้แบบเบ็ดเสร็จบรรจุอยู่ในอะไรสักอย่างที่เรียกว่าคลังโปรแกรมหรือไลบรารีที่ถือว่าเป็นมาตรฐาน (Standard Library) คลังโปรแกรมมาตรฐานมักบรรจุไว้ด้วยฟังก์ชันที่คาดหมายว่านักพัฒนาต้องการใช้เป็นแน่แท้ รวมถึงฟังก์ชันที่ใช้ในการรับและแสดงผลข้อมูล เช่น printf ที่ช่วยให้สามารถพิมพ์ข้อความหรือข้อมูลออกทางจอภาพได้อย่างสะดวก โดยไม่ต้องกังวลกับรายละเอียดเบื้องหลังการทำงานของมัน ไลบรารีมาตรฐานช่วยให้ผู้เขียนโปรแกรมสามารถใช้งานฟังก์ชันเหล่านี้ได้โดยไม่ต้องเขียนโค้ดซ้ำซ้อน

ในภาษา Python ก็สามารถพิมพ์ข้อความออกสู่จอภาพได้เช่นเดียวกับการใช้คำสั่ง printf ในภาษา C โดยใช้ฟังก์ชัน print() ซึ่งเป็นฟังก์ชันที่มีให้ใช้งานโดยตรงในตัวภาษา (built-in function) ซึ่งหมายความว่าไม่จำเป็นต้องพี่งพาการนำเข้าหรือโหลดไลบรารีใด ๆ มาก่อนเพื่อใช้งานฟังก์ชันนี้

ฟังก์ชัน print() ทำหน้าที่พิมพ์ข้อความหรือข้อมูลออกไปยังหน้าจอที่ถือเป็น standard output หรือ “มาตรฐานการแสดงผล” ซึ่งหมายถึงการแสดงผลข้อมูลที่ถูกส่งไปยังอุปกรณ์หรือสถานที่ที่ใช้ในการแสดงผลโดยตรง ในกรณีของคอมพิวเตอร์หรือเครื่องที่ใช้ Python ฟังก์ชัน print() จะพิมพ์ข้อความไปยังหน้าจอคอมพิวเตอร์หรือคอนโซลที่เปิดใช้งานโปรแกรมนั้นอยู่

ตัวอย่างเช่น การพิมพ์ข้อความ “Hello, World!” โดยใช้ฟังก์ชัน print() ใน Python สามารถทำได้โดยตรงโดยไม่ต้องนำเข้าโมดูลหรือไลบรารีใด ๆ เช่น:

print("Hello, World!")

ในภาษา C คำสั่ง #include ใช้เพื่อเพิ่มไฟล์ header ที่เก็บการประกาศของฟังก์ชัน ตัวแปร หรือข้อมูลต่าง ๆ ที่โปรแกรมสามารถใช้งานได้ในระหว่างการคอมไพล์ เช่น ฟังก์ชัน printf() ใน stdio.h หรือ malloc() ใน stdlib.h ไฟล์เหล่านี้ไม่ได้เก็บโค้ดที่โปรแกรมจะทำงานจริง แต่จะประกอบไปด้วยการประกาศที่บอกคอมไพเลอร์ว่าฟังก์ชันหรือข้อมูลเหล่านั้นมีตัวตนอยู่จริงที่ไหนสักแห่งหนึ่งซึ่งไม่จำเป็นต้องรู้ เพียงแค่ต้องเข้าใจว่าจะใช้งานได้อย่างไร

เมื่อคอมไพเลอร์พบเจอคำสั่ง #include ก็จะนำไฟล์ header ที่ระบุมาใส่ในโปรแกรมในระหว่างการคอมไพล์ ซึ่งทำให้โปรแกรมรู้จักฟังก์ชันที่ได้ประกาศไว้ในไลบรารีที่ถูกนำเข้า ตัวอย่างเช่น ถ้าใช้ #include <stdio.h> โปรแกรมสามารถใช้ฟังก์ชัน printf() เพื่อพิมพ์ข้อความออกหน้าจอได้ โดยไม่ต้องกำหนดโค้ดฟังก์ชันนั้นเอง ซึ่งช่วยให้การเขียนโปรแกรมสามารถใช้งานได้ง่ายและไม่ซับซ้อน โดยไม่ต้องเขียนโค้ดซ้ำซ้อน ทำให้การเขียนโปรแกรมรวดเร็วยิ่งขึ้น

การใช้ #include จึงถือเป็นการบอกคอมไพเลอร์ให้แทรกการประกาศที่จำเป็นเข้าไปในโปรแกรมในช่วงการคอมไพล์ เพื่อให้สามารถใช้ฟังก์ชันหรือโครงสร้างข้อมูลที่มาจากไลบรารีต่าง ๆ ได้

ในภาษา Python คำสั่ง import ใช้ในการนำเข้าโมดูลหรือไลบรารีที่มีฟังก์ชันและคลาสต่าง ๆ มาใช้งานในโปรแกรมโดยไม่ต้องเขียนโค้ดใหม่ซ้ำซ้อน ซึ่งคล้ายกับคำสั่ง #include ในภาษา C การใช้ import ช่วยให้การพัฒนาโปรแกรมสามารถใช้งานได้ง่ายและไม่ซับซ้อน เนื่องจากสามารถนำฟังก์ชันจากไลบรารีที่มีอยู่แล้วมาใช้งานได้ทันที โดยไม่ต้องเสียเวลาในการพัฒนาโค้ดซ้ำ นอกจากนี้ยังช่วยให้โค้ดดูมีระเบียบและกระชับขึ้น โดยไม่จำเป็นต้องใช้โค้ดที่ซ้ำซ้อนหรือยุ่งยาก

เมื่อใช้คำสั่ง import ใน Python โปรแกรมสามารถเข้าถึงฟังก์ชัน ตัวแปร หรือคลาสต่าง ๆ ที่มีอยู่ในโมดูลที่นำเข้ามา ตัวอย่างเช่น การใช้คำสั่ง import time เพื่อใช้งานฟังก์ชัน sleep() ซึ่งทำให้โปรแกรมหยุดทำงานชั่วขณะหนึ่งตามเวลาที่กำหนดฟังก์ชันนี้อยู่ในโมดูล time และสามารถกำหนดระยะเวลาในการหยุดทำงานได้เป็นหน่วยวินาที ตัวอย่างเช่น:

import time  # นำเข้าโมดูล time เพื่อใช้งานฟังก์ชันที่เกี่ยวข้องกับการทำงานที่เกี่ยวกับเวลา

print("Start") # พิมพ์ข้อความ "Start" ก่อน
time.sleep(2) # หยุดการทำงานของโปรแกรมเป็นเวลา 2 วินาที โดยใช้ ฟังก์ชัน sleep() ของโมดูล time
print("End") # พิมพ์ข้อความ "End" หลังจากหยุดทำงาน

ในภาษา Python คำสั่ง import ช่วยให้โปรแกรมสามารถใช้งานฟังก์ชัน, ตัวแปร, หรือคลาสต่าง ๆ ที่มีอยู่ในไลบรารีหรือโมดูลที่นำเข้ามา โดยไม่จำเป็นต้องเขียนโค้ดใหม่ ซึ่งเป็นการลดความซับซ้อนในการเขียนโปรแกรมและช่วยให้โค้ดสามารถใช้งานฟังก์ชันที่พัฒนาขึ้นแล้วจากแหล่งอื่น ๆ ได้ง่ายดาย

เมื่อโปรแกรมใช้คำสั่ง import เพื่อดึงโมดูลเข้ามาใช้งาน ระบบจะไม่ได้ทำการคอมไพล์หรือโหลดโค้ดทั้งหมดจากโมดูลในทันที แต่จะโหลดเฉพาะส่วนของโค้ดที่ถูกเรียกใช้งานจริงในขณะนั้น กล่าวคือ โปรแกรมจะดึงเฉพาะฟังก์ชันหรือคลาสที่จำเป็นมาใช้ โดยส่วนของโค้ดที่ไม่ได้ถูกเรียกใช้งานจะยังไม่ถูกโหลดเข้ามาในหน่วยความจำ กระบวนการนี้ช่วยเพิ่มประสิทธิภาพการทำงานของโปรแกรม ลดการใช้ทรัพยากร และช่วยประหยัดเวลาในการประมวลผลข้อมูลที่ไม่จำเป็น

การโหลด ในบริบทนี้ หมายถึงกระบวนการนำโค้ดหรือข้อมูลจากไฟล์โมดูลภายนอกเข้าสู่หน่วยความจำ (Memory) เพื่อให้โปรแกรมสามารถเรียกใช้งานได้ กระบวนการนี้ประกอบด้วยการค้นหาไฟล์โมดูล การอ่านและคอมไพล์โค้ดให้เป็นไบต์โค้ด การสร้างออบเจ็กต์ของโมดูลในหน่วยความจำ และการจัดการแคชเพื่อป้องกันการโหลดซ้ำ กระบวนการดังกล่าวช่วยให้โปรแกรมทำงานได้อย่างรวดเร็วและใช้ทรัพยากรระบบอย่างมีประสิทธิภาพ

ตัวอย่างเช่น เมื่อใช้คำสั่ง import time สามารถเรียกใช้ฟังก์ชัน sleep() จากโมดูลนี้ได้ หากไม่ใช้ฟังก์ชันอื่น ๆ ในโมดูล time เช่น time.time() หรือ time.localtime() โค้ดเหล่านั้นจะไม่ถูกโหลดเข้ามาใช้งาน ทำให้โปรแกรมทำงานได้อย่างมีประสิทธิภาพและไม่ต้องโหลดโค้ดที่ไม่จำเป็น

🌟 การนำเข้าโมดูลโดยใช้ Import 🌟

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

ใน Python การนำเข้าโมดูลมีหลายวิธีที่สามารถเลือกใช้ตามความเหมาะสมของโปรแกรมและลักษณะการทำงานที่ต้องการ

✅ การนำเข้าโมดูลทั้งหมด (Importing the entire module)

การนำเข้าโมดูลทั้งหมดหมายถึงการนำเข้าโมดูลให้สามารถใช้งานฟังก์ชันหรือคลาสทั้งหมดจากโมดูลนั้นได้โดยตรงในโปรแกรม ตัวอย่างเช่น เมื่อใช้ import time ก็จะสามารถเข้าถึงฟังก์ชันทั้งหมดในโมดูล time โดยไม่จำเป็นต้องระบุฟังก์ชันเฉพาะเจาะจงก่อนที่จะใช้ เช่น time.sleep() หรือ time.time() ซึ่งจะทำให้โปรแกรมสามารถใช้ฟังก์ชันเหล่านี้ได้ทันที

import time

# ใช้ฟังก์ชัน sleep() จากโมดูล time
print("เริ่มรอ...")
time.sleep(2) # หยุดการทำงานของโปรแกรมเป็นเวลา 2 วินาที
print("เสร็จสิ้นการรอ")
# ใช้ฟังก์ชัน time() จากโมดูล time
current_time = time.time()
print("เวลาปัจจุบันเป็น:", current_time)

เมื่อทำการนำเข้าโมดูลทั้งหมดด้วยคำสั่ง import time ใน Python จะสามารถใช้ฟังก์ชันต่างๆ ที่มีอยู่ในโมดูล time ได้อย่างสะดวกสบาย เช่น time.sleep() สำหรับการหยุดการทำงานของโปรแกรมตามระยะเวลา หรือ time.time() เพื่อดึงเวลาปัจจุบันในรูปแบบจำนวนวินาทีที่ผ่านไปตั้งแต่วันที่ 1 มกราคม 1970 (epoch time) โดยไม่ต้องระบุชื่อฟังก์ชันอย่างละเอียดในทุกครั้งที่ใช้ ตัวอย่างเช่น เมื่อใช้ time.sleep(2) โปรแกรมจะหยุดทำงานเป็นเวลา 2 วินาที และ time.time() จะให้ค่าของเวลาปัจจุบันในหน่วยวินาทีจาก epoch time ทำให้โค้ดเรียบง่ายและกระชับมากขึ้น

การนำเข้าโมดูลทั้งหมดเหมาะสมในกรณีที่ต้องการใช้งานฟังก์ชันหลายๆ ตัวจากโมดูลนั้นๆ เช่น ในกรณีของ time ที่มีฟังก์ชันหลากหลายที่อาจถูกใช้ในโปรแกรมเดียวกัน โดยไม่ต้องไปหาวิธีเรียกใช้ฟังก์ชันเฉพาะเจาะจงในแต่ละบรรทัดของโค้ด อย่างไรก็ตามการนำเข้าโมดูลทั้งหมดก็มีข้อเสียในเรื่องของการใช้หน่วยความจำ หากโมดูลนั้นมีฟังก์ชันหรือคลาสจำนวนมากที่ไม่ได้ใช้ทั้งหมด ก็อาจทำให้โปรแกรมใช้หน่วยความจำมากขึ้นโดยไม่จำเป็น นอกจากนี้ยังมีโอกาสที่ชื่อของฟังก์ชันหรือคลาสในโมดูลที่นำเข้าอาจซ้ำกับชื่อที่ใช้ในโปรแกรม ซึ่งอาจทำให้เกิดข้อผิดพลาดในการทำงานของโปรแกรมได้

โดยทั่วไปแล้ว การนำเข้าโมดูลทั้งหมดจะเป็นทางเลือกที่ดีในกรณีที่ต้องการใช้ฟังก์ชันหลายๆ ตัวจากโมดูลนั้นๆ และไม่ต้องการเสียเวลาหรือซับซ้อนในการระบุชื่อฟังก์ชันในแต่ละบรรทัด แต่ต้องคำนึงถึงขนาดของโมดูลและการใช้หน่วยความจำรวมถึงการระวังไม่ให้เกิดการทับซ้อนของชื่อฟังก์ชันที่อาจทำให้เกิดปัญหาในโปรแกรมได้

✅ การนำเข้าฟังก์ชันหรือคลาสเฉพาะจากโมดูล (Importing specific functions or classes)

การนำเข้าฟังก์ชันหรือคลาสเฉพาะจากโมดูลช่วยให้โค้ดมีความกระชับและประสิทธิภาพมากขึ้น เนื่องจากไม่จำเป็นต้องนำเข้าโมดูลทั้งหมด หากต้องการใช้งานแค่บางฟังก์ชันหรือคลาสจากโมดูลนั้นๆ การใช้คำสั่ง from <module> import <function_or_class> จะช่วยให้สามารถเลือกนำเข้าฟังก์ชันหรือคลาสที่ต้องการใช้งานได้โดยตรง ซึ่งทำให้โปรแกรมทำงานได้เร็วขึ้นและใช้หน่วยความจำน้อยลง

ตัวอย่างเช่น เมื่อต้องการใช้ฟังก์ชัน sleep จากโมดูล time:

from time import sleep
# ใช้ฟังก์ชัน sleep() เพื่อหยุดการทำงานของโปรแกรมชั่วขณะ
print("เริ่มรอ...")
sleep(2) # หยุดการทำงานของโปรแกรมเป็นเวลา 2 วินาที
print("เสร็จสิ้นการรอ"))

ในตัวอย่างนี้ ฟังก์ชัน sleep จากโมดูล time โดยไม่ต้องนำเข้าโมดูล time ทั้งหมด สามารถเรียกใช้ฟังก์ชัน sleep() ได้โดยตรงโดยไม่ต้องระบุชื่อของโมดูล time ซึ่งทำให้โค้ดสามารถใช้งานได้ง่ายและไม่ซับซ้อน โดยไม่ต้องเสียเวลาในการพิมพ์ชื่อโมดูลหรือฟังก์ชันซ้ำๆ ช่วยลดความยุ่งยากในการเขียนโค้ด ทำให้โค้ดดูกระชับขึ้น

✅ การนำเข้าและตั้งชื่อใหม่ให้กับฟังก์ชันหรือโมดูล

บางครั้งอาจต้องการนำเข้าโมดูลหรือฟังก์ชันและตั้งชื่อให้สั้นลงหรือเหมาะสมกับการใช้งาน โดยเฉพาะในกรณีที่ชื่อโมดูลหรือฟังก์ชันมีความยาวหรือซับซ้อน การตั้งชื่อย่อจะทำให้โค้ดดูเรียบง่ายและอ่านง่ายขึ้นโดยใช้คำสั่ง import ... as ... ตัวอย่างเช่น:

การตั้งชื่อย่อ (Aliasing) สำหรับโมดูล time ช่วยให้โค้ดสั้นลงและใช้งานได้ง่ายขึ้น เพราะไม่จำเป็นต้องพิมพ์ชื่อโมดูลเต็มทุกครั้งที่ต้องการใช้ฟังก์ชันจากโมดูลนั้น ตัวอย่างเช่น หากต้องการใช้ฟังก์ชัน sleep() จากโมดูล time และต้องการให้ชื่อโมดูลสั้นลง ก็สามารถตั้งชื่อย่อให้โมดูล time ด้วยคำสั่ง import time as t ดังนี้

import time as t
# ใช้ฟังก์ชัน sleep() จากโมดูล time ที่ตั้งชื่อย่อเป็น t
print("เริ่มรอ...")
t.sleep(2) # หยุดการทำงานของโปรแกรมเป็นเวลา 2 วินาที
print("เสร็จสิ้นการรอ")

ในตัวอย่างนี้ ทำการนำเข้าโมดูล time โดยใช้ชื่อย่อว่า t ซึ่งทำให้สามารถใช้ฟังก์ชัน sleep() ได้ง่ายขึ้นโดยไม่ต้องพิมพ์ time.sleep() ทุกครั้ง เพียงแค่ใช้ t.sleep() แทน

การตั้งชื่อย่อแบบนี้ช่วยให้โค้ดดูสะอาดและกระชับขึ้น โดยเฉพาะเมื่อต้องใช้ฟังก์ชันจากโมดูลหลายๆ ครั้งในโปรแกรม เช่น หากใช้ time.sleep() หรือ time.time() บ่อยๆ การตั้งชื่อย่อจะทำให้โค้ดดูเรียบร้อยขึ้น และยังช่วยลดการพิมพ์ชื่อยาวๆ ซ้ำๆ อีกด้วย

การตั้งชื่อย่อ (Aliasing) สำหรับโมดูล time ช่วยให้โค้ดสั้นลงและใช้งานได้ง่ายขึ้น เพราะไม่จำเป็นต้องพิมพ์ชื่อโมดูลเต็มทุกครั้งที่ต้องการใช้ฟังก์ชันจากโมดูลนั้น ตัวอย่างเช่น หากต้องการใช้ฟังก์ชัน sleep() จากโมดูล time และต้องการให้ชื่อโมดูลสั้นลง ก็สามารถตั้งชื่อย่อให้โมดูล time ด้วยคำสั่ง import time as t ดังนี้

การใช้คำสั่ง from <module> import <function_or_class> โดยไม่มีการกำหนดชื่อย่อ (alias) อาจทำให้เกิดปัญหาการทับซ้อนของชื่อฟังก์ชันหรือคลาส หากนำเข้าฟังก์ชันที่มีชื่อเดียวกันจากหลายโมดูล ซึ่งจะทำให้ Python ไม่สามารถแยกแยะได้ว่าต้องการใช้ฟังก์ชันจากโมดูลไหน ตัวอย่างเช่น:

from math import fsum
from statistics import fsum

numbers = [0.1, 0.2, 0.3, 0.4, 0.5]
# ปัญหาการทับซ้อนของชื่อ
result = fsum(numbers) # ฟังก์ชัน fsum() จากโมดูลใด?
print(result)

ในกรณีนี้, Python จะใช้ฟังก์ชัน fsum() จากโมดูลที่ถูกนำเข้าล่าสุด (ในที่นี้คือ statistics) ซึ่งอาจทำให้เกิดปัญหาเมื่อต้องการใช้ฟังก์ชันจากทั้งสองโมดูล หากไม่ระบุความชัดเจนเกี่ยวกับฟังก์ชันที่ต้องการใช้

การแก้ปัญหานี้สามารถทำได้โดยการใช้ as เพื่อกำหนดชื่อย่อ (alias) ให้กับฟังก์ชันจากแต่ละโมดูล เพื่อให้สามารถระบุได้ชัดเจนว่าใช้ฟังก์ชันจากโมดูลไหน ตัวอย่างเช่น:

from math import fsum as math_fsum
from statistics import fsum as stats_fsum

numbers = [0.1, 0.2, 0.3, 0.4, 0.5]
# ใช้ฟังก์ชัน fsum() จาก math
result_math = math_fsum(numbers)
print(f"ผลรวมจาก math.fsum({numbers}) = {result_math}")
# ใช้ฟังก์ชัน fsum() จาก statistics
result_statistics = stats_fsum(numbers)
print(f"ผลรวมจาก statistics.fsum({numbers}) = {result_statistics}")

ในตัวอย่างนี้, ฟังก์ชัน fsum() จาก math ถูกกำหนดชื่อย่อเป็น math_fsum และฟังก์ชัน fsum() จาก statistics ถูกกำหนดชื่อย่อเป็น stats_fsum ซึ่งทำให้สามารถใช้งานฟังก์ชันจากทั้งสองโมดูลได้อย่างชัดเจน โดยไม่เกิดปัญหาการทับซ้อนของชื่อฟังก์ชัน

✅ การนำเข้าหลายฟังก์ชันจากโมดูลเดียว

การนำเข้าหลายฟังก์ชันจากโมดูลเดียวช่วยให้โค้ดสั้นลงและอ่านง่ายขึ้น โดยไม่ต้องพิมพ์ชื่อโมดูลซ้ำๆ หลายครั้ง ซึ่งทำให้โค้ดดูสะอาดและมีความกระชับมากขึ้น ตัวอย่างการใช้คำสั่ง from ... import ... จะช่วยให้สามารถนำเข้าฟังก์ชันหรือคลาสจากโมดูลได้หลายๆ ตัวพร้อมกัน แทนที่จะต้องใช้คำสั่ง import แยกกันหลายบรรทัด การนำเข้าหลายฟังก์ชันจากโมดูลเดียวมีข้อดีหลายประการ เช่น:

  • ประหยัดพื้นที่โค้ด: ไม่ต้องใช้คำสั่ง import ซ้ำๆ สำหรับแต่ละฟังก์ชัน เช่น ถ้าต้องการใช้หลายฟังก์ชันจากโมดูลเดียว เช่น sleep()time(), และ localtime() จากโมดูล time ก็สามารถนำเข้าทั้งหมดในบรรทัดเดียว:
  • ความชัดเจน: เมื่อนำเข้าฟังก์ชันหลายตัวจากโมดูลเดียวแล้ว จะทำให้โค้ดดูเรียบง่าย และสามารถเรียกใช้ฟังก์ชันเหล่านั้นได้โดยตรง โดยไม่ต้องระบุชื่อโมดูลซ้ำๆ ทุกครั้ง
  • การจัดการที่สะดวก: เมื่อใช้ฟังก์ชันหลายตัวจากโมดูลเดียว การนำเข้าทั้งหมดในบรรทัดเดียวจะทำให้การจัดการโค้ดในโปรแกรมง่ายขึ้น และช่วยให้สามารถเห็นฟังก์ชันที่นำเข้าได้ชัดเจน
from time import sleep, time, localtime

# ใช้ sleep เพื่อหยุดการทำงานของโปรแกรมเป็นเวลา 2 วินาที
sleep(2)
# ใช้ time เพื่อดึงเวลาปัจจุบันในรูปแบบ epoch time
current_time = time()
print("เวลาปัจจุบัน (Epoch Time):", current_time)
# ใช้ localtime เพื่อแปลงเวลาปัจจุบันให้เป็นเวลาในรูปแบบที่เข้าใจง่าย
local_time = localtime(current_time)
print("เวลาท้องถิ่น:", local_time)

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

✅ การนำเข้าโมดูลย่อย (Submodules)

การนำเข้าโมดูลย่อย (Submodules) ใน Python หมายถึงการที่โมดูลหลักประกอบไปด้วยโมดูลย่อยหลายตัว ซึ่งแต่ละโมดูลย่อยนั้นอาจมีฟังก์ชันหรือคลาสเฉพาะที่ช่วยให้การทำงานมีความยืดหยุ่นและแยกการทำงานให้ชัดเจนขึ้น

ในกรณีที่โมดูลหลักมีหลายฟังก์ชันที่เกี่ยวข้องกับฟังก์ชันย่อยต่างๆ การนำเข้าโมดูลย่อยจะช่วยให้สามารถเข้าถึงฟังก์ชันหรือคลาสที่ต้องการโดยไม่จำเป็นต้องนำเข้าทั้งหมด

ตัวอย่างที่ดีคือโมดูล os ซึ่งเป็นโมดูลที่ใช้ในการจัดการกับระบบไฟล์หรือการทำงานเกี่ยวกับระบบปฏิบัติการ ในโมดูล os จะมีโมดูลย่อยหลายตัว เช่น os.pathos.system ซึ่งแต่ละตัวมีหน้าที่ที่แตกต่างกัน

  • การนำเข้าโมดูลย่อยจาก os: สมมติว่าต้องการใช้ฟังก์ชันจากโมดูลย่อย os ใน MicroPython เช่น os.path สำหรับการจัดการกับไฟล์บนหน่วยความจำของไมโครคอนโทรลเลอร์
import os
from os.path import join, exists

จากนั้นสามารถใช้ฟังก์ชันเหล่านี้ในการจัดการเส้นทางไฟล์บนหน่วยความจำหรือ SD card เช่น:

# กำหนดเส้นทางไฟล์
file_path = join("folder", "file.txt")

# ตรวจสอบว่าไฟล์มีอยู่หรือไม่
if exists(file_path):
print("File exists.")
else:
print("File does not exist.")
  • การใช้ os ในการจัดการไฟล์ : ใน MicroPython โมดูล os ยังสามารถใช้สำหรับจัดการไฟล์ได้ เช่น การสร้างไฟล์ใหม่หรืออ่านข้อมูลจากไฟล์ที่มีอยู่
import os

# สร้างไฟล์ใหม่และเขียนข้อมูล
with open('file.txt', 'w') as f:
f.write('Hello, MicroPython!')
# อ่านข้อมูลจากไฟล์
with open('file.txt', 'r') as f:
content = f.read()
print(content)
  • การใช้ os ในการตรวจสอบการมีอยู่ของไดเรกทอรี : การใช้ฟังก์ชันจาก os เช่น os.listdir() สำหรับการตรวจสอบไฟล์และไดเรกทอรีใน MicroPython:
import os

# ตรวจสอบรายการไฟล์ในไดเรกทอรีปัจจุบัน
files = os.listdir()
print("Files in current directory:", files)
# ตรวจสอบว่าไดเรกทอรี 'folder' มีอยู่หรือไม่
if 'folder' in files:
print("Directory 'folder' exists.")
else:
print("Directory 'folder' does not exist.")

การใช้โมดูลย่อยใน MicroPython มีข้อดีหลายประการที่ช่วยเพิ่มประสิทธิภาพของโค้ด โดยเฉพาะในการจัดการหน่วยความจำและการเขียนโค้ดให้มีความคล่องตัวมากขึ้น การเลือกนำเข้าเฉพาะฟังก์ชันที่จำเป็นจากโมดูลย่อย ช่วยให้โปรแกรมทำงานได้รวดเร็วและประหยัดทรัพยากรของไมโครคอนโทรลเลอร์ เนื่องจากไม่จำเป็นต้องโหลดฟังก์ชันที่ไม่ได้ใช้เข้ามาในหน่วยความจำ ตัวอย่างเช่น การนำเข้าเฉพาะฟังก์ชันที่ต้องการจาก os.path (เช่น os.path.join()) จะช่วยให้ไม่ต้องโหลดฟังก์ชันที่ไม่ได้ใช้งาน เช่น os.remove() หรือ os.rename() ซึ่งอาจไม่จำเป็นในโปรแกรม การทำเช่นนี้ทำให้โค้ดมีความยืดหยุ่นและสามารถจัดการได้ง่ายกว่า เพราะไม่ต้องพิมพ์ชื่อโมดูลซ้ำ ๆ และสามารถเลือกใช้ฟังก์ชันที่จำเป็นเพียงบางส่วนได้ทันที

การเลือกนำเข้าแค่ฟังก์ชันที่ต้องการใช้งานยังช่วยให้โค้ดกระชับและอ่านง่ายขึ้น ไม่ต้องพิมพ์ชื่อโมดูลซ้ำๆ ทุกครั้ง เช่น os.path.join() หรือ os.path.exists() ซึ่งแทนที่จะพิมพ์ชื่อโมดูลยาวๆ สามารถใช้ชื่อฟังก์ชันได้โดยตรง เช่น join() หรือ exists() ทำให้โค้ดสะอาดตาและเข้าใจได้ง่ายขึ้น

✅ การนำเข้าไลบรารีภายนอก (Importing external libraries)

การนำเข้าไลบรารีภายนอกหมายถึงการใช้งานไลบรารีหรือโมดูลที่ไม่ได้เป็นส่วนหนึ่งของไลบรารีมาตรฐานของ Python หรือ MicroPython ซึ่งต้องติดตั้งจากแหล่งภายนอก เช่น PyPI หรือที่เก็บโค้ดอื่น ๆ ที่รองรับการใช้งานในโปรเจค การนำเข้าไลบรารีภายนอกใน MicroPython จะช่วยให้สามารถใช้ฟังก์ชันและเครื่องมือที่ไลบรารีเหล่านั้นมีให้ในการพัฒนาโปรเจคได้

ใน MicroPython การนำเข้าไลบรารีภายนอกจะต้องติดตั้งไลบรารีผ่านเครื่องมือที่เรียกว่า upip (MicroPython package manager) หรือดาวน์โหลดไฟล์ไลบรารีและนำไปวางในไฟล์ระบบของอุปกรณ์ไมโครคอนโทรลเลอร์ เช่น Raspberry Pi Pico หรือ ESP32

ตัวอย่างกรณีของ mpy: ตัวอย่างที่ดีในการใช้งานไลบรารีภายนอกใน MicroPython คือการใช้ umqtt.simple ซึ่งเป็นไลบรารีสำหรับการเชื่อมต่อและใช้งาน MQTT (Message Queuing Telemetry Transport) ซึ่งเป็นโปรโตคอลที่นิยมใช้ในการสื่อสารระหว่างอุปกรณ์ IoT

💜 การติดตั้งไลบรารี:

สามารถติดตั้งไลบรารีภายนอกใน MicroPython ผ่านคำสั่ง upip ดังนี้:

import upip
upip.install('micropython-umqtt.simple')

คำสั่งนี้จะทำการดาวน์โหลดและติดตั้งไลบรารี micropython-umqtt.simple จากแหล่งเก็บไลบรารีภายนอกของ MicroPython

💜 การนำเข้าและใช้งานไลบรารี:

หลังจากติดตั้งไลบรารีแล้ว ก็จะสามารถนำเข้าและใช้งานฟังก์ชันต่างๆ จากไลบรารีนั้นได้ ตัวอย่างเช่น การเชื่อมต่อกับ MQTT broker:

import umqtt.simple as mqtt

# กำหนดข้อมูลการเชื่อมต่อ
client = mqtt.MQTTClient("client_id", "mqtt_broker_address")
client.connect()
# การส่งข้อความ
client.publish("topic", "Hello, MQTT!")
# การรับข้อความ
client.subscribe("topic")
client.wait_msg() # รอข้อความจาก broker

ในตัวอย่างนี้ mqtt.MQTTClient เป็นคำสั่งที่ใช้สร้างการเชื่อมต่อระหว่างไมโครคอนโทรลเลอร์กับ MQTT broker ซึ่งเป็นเซิร์ฟเวอร์ที่ทำหน้าที่ในการส่งและรับข้อความระหว่างอุปกรณ์ต่างๆ ส่วน client.connect() เป็นคำสั่งที่ใช้ในการเชื่อมต่อกับ broker โดยที่ต้องระบุที่อยู่ของ broker เช่น IP address หรือ domain name ของเซิร์ฟเวอร์นั้น ๆ เมื่อเชื่อมต่อกับ broker ได้แล้ว สามารถใช้ client.publish() เพื่อส่งข้อความไปยังหัวข้อ (topic) ที่กำหนด ซึ่งหัวข้อนี้จะระบุว่ากำลังส่งข้อมูลไปยังส่วนใดของระบบ เช่น การส่งข้อมูลเซ็นเซอร์หรือคำสั่งต่าง ๆ ไปยังอุปกรณ์อื่น ๆ ที่เชื่อมต่อกับ broker เดียวกัน ส่วน client.subscribe() ใช้เพื่อสมัครรับข้อมูลจากหัวข้อนั้น ๆ เมื่อมีข้อมูลใหม่ถูกส่งไปยังหัวข้อที่สมัครรับ ระบบจะสามารถรับข้อความเหล่านั้นได้โดยอัตโนมัติ ทั้งหมดนี้ช่วยให้การสื่อสารระหว่างอุปกรณ์ที่ใช้งาน MQTT เป็นไปอย่างมีประสิทธิภาพและรวดเร็ว

การใช้ไลบรารีภายนอกใน MicroPython ช่วยให้สามารถเข้าถึงฟังก์ชันและเครื่องมือที่มีประโยชน์มากมาย เช่น การเชื่อมต่อกับเครือข่าย การทำงานกับเซ็นเซอร์ หรือการควบคุมอุปกรณ์ IoT โดยไม่ต้องพัฒนาโค้ดทั้งหมดตั้งแต่เริ่มต้น ซึ่งช่วยประหยัดเวลาในการพัฒนาและทำให้โปรแกรมทำงานได้เร็วขึ้น เพราะไลบรารีเหล่านี้มักจะถูกพัฒนาและทดสอบอย่างดีแล้ว การนำมาใช้จึงมั่นใจได้ว่าฟังก์ชันที่มีอยู่จะทำงานได้อย่างถูกต้อง

นอกจากนี้ การเลือกใช้งานไลบรารีที่เหมาะสมยังช่วยให้การจัดการทรัพยากรต่างๆ ใน MicroPython เช่น หน่วยความจำและพลังงาน เป็นไปอย่างมีประสิทธิภาพ โดยเฉพาะในอุปกรณ์ที่มีข้อจำกัดในด้านเหล่านี้ อย่างไรก็ตาม ควรระมัดระวังในเรื่องของข้อจำกัดของหน่วยความจำ เพราะ MicroPython มักจะทำงานบนอุปกรณ์ที่มีหน่วยความจำค่อนข้างจำกัด การเลือกใช้ไลบรารีที่มีขนาดเล็กและมีประสิทธิภาพจะช่วยลดปัญหานี้ได้

การติดตั้งไลบรารีภายนอกบางตัวอาจต้องใช้เครื่องมือพิเศษ เช่น upip ซึ่งช่วยให้การติดตั้งไลบรารีจากอินเทอร์เน็ตเป็นไปได้ หรือบางครั้งอาจต้องดาวน์โหลดไลบรารีในรูปแบบ .mpy ซึ่งเป็นไฟล์ไลบรารีที่ถูกคอมไพล์แล้วเพื่อให้สามารถใช้งานได้ใน MicroPython ทำให้การใช้งานไลบรารีภายนอกสะดวกและรวดเร็วขึ้น แต่ก็ต้องระมัดระวังในการเลือกใช้ให้เหมาะสมกับข้อจำกัดของอุปกรณ์ที่ใช้งาน

✅ การนำเข้าโมดูลที่เป็นโครงสร้างระบบไฟล์ (File System Structure)

เป็นกระบวนการสำคัญในการจัดการโปรเจคที่มีหลายไฟล์หรือหลายโฟลเดอร์ โดยช่วยให้สามารถเข้าถึงโมดูลต่างๆ ได้อย่างสะดวกและเป็นระเบียบ การนำเข้าโมดูลใน Python หรือ MicroPython มักจะใช้เส้นทาง (path) เพื่อระบุที่ตั้งของโมดูลที่ต้องการนำเข้าจากไฟล์หรือโฟลเดอร์ต่างๆ ในระบบไฟล์

เมื่อโปรเจคมีความซับซ้อนและประกอบด้วยหลายไฟล์หรือหลายโฟลเดอร์ การจัดระเบียบโครงสร้างไฟล์จะช่วยให้การนำเข้าโมดูลทำได้ง่ายและมีประสิทธิภาพมากขึ้น โดยเฉพาะในกรณีที่โปรเจคมีโมดูลหลายตัวที่ต้องการใช้งานร่วมกันในโปรเจคเดียวกัน ซึ่งจะช่วยให้การเข้าถึงโมดูลต่างๆ เป็นไปอย่างมีระเบียบและสะดวกยิ่งขึ้น

การกำหนดเส้นทาง (Path) ในการนำเข้าโมดูลเป็นอีกหนึ่งกระบวนการที่มีความสำคัญ เมื่อเราทำงานกับโปรเจคที่มีหลายไฟล์หรือหลายโฟลเดอร์ โครงสร้างของไฟล์นั้นๆ จะมีบทบาทสำคัญในการจัดการและนำเข้าโมดูลให้ทำงานได้อย่างถูกต้อง การใช้เส้นทางหรือโครงสร้างของไฟล์ (Directory Structure) ช่วยให้ Python หรือ MicroPython รู้ว่าโมดูลนั้นอยู่ที่ไหนในโปรเจค และสามารถนำเข้าโมดูลจากที่ต่างๆ ได้อย่างถูกต้องตามเส้นทางที่กำหนด

ตัวอย่างการกำหนดโครงสร้างไฟล์ : สมมติว่าโปรเจคของมีโครงสร้างไฟล์ดังนี้:

/my_project
├── main.py # ไฟล์หลักที่เริ่มต้นโปรเจค
├── lib/ # โฟลเดอร์เก็บโมดูลหลักของโปรเจค
│ ├── __init__.py # ไฟล์ __init__.py สำหรับทำให้ lib/ เป็นแพ็กเกจ
│ ├── mymodule.py # โมดูล mymodule
│ ├── utils.py # โมดูล utils
│ └── helper/ # โฟลเดอร์ย่อยสำหรับโมดูลเพิ่มเติม
│ ├── __init__.py # ไฟล์ __init__.py ใน helper/ สำหรับทำให้ helper/ เป็นแพ็กเกจ
│ ├── submodule1.py # โมดูลย่อย submodule1 ใน helper/
│ └── submodule2.py # โมดูลย่อย submodule2 ใน helper/
├── config.py # ไฟล์คอนฟิกเก็บค่าคอนฟิกต่างๆ
└── tests/ # โฟลเดอร์เก็บไฟล์ทดสอบ
├── __init__.py # ไฟล์ __init__.py สำหรับทำให้ tests/ เป็นแพ็กเกจ
├── test_mymodule.py # ไฟล์ทดสอบสำหรับ mymodule
└── test_utils.py # ไฟล์ทดสอบสำหรับ utils

ในโครงสร้างไฟล์ของโปรเจคที่ได้กล่าวถึงนั้น, ไฟล์ main.py เป็นไฟล์หลักที่ใช้เริ่มต้นโปรเจคและทำหน้าที่เชื่อมโยงกับโมดูลต่างๆ ที่เก็บไว้ในโฟลเดอร์ lib/ เช่น mymodule.py และ utils.py ซึ่งช่วยให้การจัดการโปรเจคและการแยกโค้ดออกเป็นโมดูลต่างๆ ง่ายขึ้น การใช้ไฟล์ main.py ช่วยให้โค้ดสะดวกในการแก้ไขและดูแลรักษา เพราะสามารถนำเข้าโมดูลต่างๆ มาทำงานร่วมกันได้อย่างเป็นระเบียบ โดยไม่ต้องระบุเส้นทางไฟล์ที่ซับซ้อน

ในโฟลเดอร์ lib/ จะมีไฟล์โมดูลหลัก เช่น mymodule.py และ utils.py ที่เก็บฟังก์ชันและคลาสที่ใช้ในโปรเจค นอกจากนี้ยังมีโฟลเดอร์ย่อย helper/ ที่เก็บโมดูลเสริมที่สามารถนำมาใช้เพิ่มเติมได้ตามความต้องการ ไฟล์ __init__.py ที่อยู่ในโฟลเดอร์ lib/ ทำหน้าที่ทำให้โฟลเดอร์นี้กลายเป็นแพ็กเกจ ช่วยให้สามารถนำเข้าโมดูลจากภายในโฟลเดอร์นี้ได้โดยไม่ต้องระบุเส้นทางไฟล์ทั้งหมด อีกทั้งในโฟลเดอร์ helper/ ยังมีไฟล์ __init__.py ซึ่งทำให้โฟลเดอร์นี้กลายเป็นแพ็กเกจย่อย ช่วยให้สามารถนำเข้าโมดูลจาก helper/ ได้สะดวกเช่นเดียวกัน

ไฟล์ config.py ใช้สำหรับเก็บค่าคอนฟิกต่างๆ ที่จำเป็นต่อการทำงานของโปรเจค เช่น การตั้งค่าการเชื่อมต่อฐานข้อมูล หรือค่าพารามิเตอร์อื่นๆ ที่ต้องใช้ในหลายๆ จุดในโปรเจค การจัดเก็บค่าคอนฟิกในไฟล์นี้ทำให้สามารถเข้าถึงและปรับเปลี่ยนค่าต่างๆ ได้อย่างสะดวก โดยไม่ต้องกำหนดค่าเหล่านั้นซ้ำในแต่ละไฟล์

ในโฟลเดอร์ tests/ จะเก็บไฟล์ทดสอบที่ใช้ทดสอบฟังก์ชันหรือคลาสที่มีใน mymodule.py หรือ utils.py ซึ่งสามารถใช้เครื่องมือทดสอบต่างๆ เช่น unittest หรือ pytest ในการทดสอบ ไฟล์ __init__.py ในโฟลเดอร์ tests/ ช่วยให้โฟลเดอร์นี้กลายเป็นแพ็กเกจ ทำให้สามารถนำเข้าโมดูลทดสอบจากภายในได้อย่างสะดวกและเป็นระเบียบ

ไฟล์ __init__.py มีบทบาทสำคัญในการทำให้โฟลเดอร์ที่เก็บโมดูลต่างๆ กลายเป็น “แพ็กเกจ” ในระบบโมดูลของ Python หรือ MicroPython ซึ่งช่วยให้การนำเข้าโมดูลภายในโฟลเดอร์นั้นสะดวกขึ้น โดยไม่จำเป็นต้องระบุเส้นทางไฟล์ที่ยาวหรือซับซ้อน การใช้ไฟล์ __init__.py ช่วยให้โครงสร้างโปรเจคมีความชัดเจนและสามารถจัดการได้ง่ายขึ้น เนื่องจากเราสามารถนำเข้าโมดูลจากแพ็กเกจได้โดยตรง เช่น การนำเข้าโมดูลจาก lib/ ผ่านคำสั่ง import lib.mymodule โดยไม่ต้องระบุเส้นทางที่ซับซ้อน

ไฟล์ __init__.py สามารถเป็นไฟล์ว่างได้ เพราะหน้าที่หลักของไฟล์นี้คือการบ่งบอกให้ Python หรือ MicroPython รู้ว่าโฟลเดอร์นั้นเป็นแพ็กเกจ ซึ่งทำให้ Python หรือ MicroPython สามารถนำเข้าโมดูลจากโฟลเดอร์นี้ได้โดยตรง ในกรณีที่ไฟล์ __init__.py เป็นไฟล์ว่าง โฟลเดอร์นั้นก็ยังคงทำหน้าที่เป็นแพ็กเกจได้ตามปกติ และ Python หรือ MicroPython จะรู้ว่าโฟลเดอร์นั้นมีโมดูลที่สามารถนำเข้าได้ผ่านแพ็กเกจ

การใช้ไฟล์ __init__.py ในโครงสร้างโปรเจคทำให้โค้ดดูสะอาดตาและเข้าใจง่ายขึ้น การจัดการโมดูลในโฟลเดอร์เดียวกันจะเป็นระเบียบมากขึ้น และทำให้สามารถนำเข้าโมดูลหรือฟังก์ชันที่ต้องการได้โดยไม่ต้องระบุเส้นทางอย่างละเอียด ทั้งหมดนี้ช่วยให้โครงสร้างของโปรเจคมีความเป็นระเบียบ สามารถจัดการและดูแลรักษาโค้ดได้ง่ายขึ้น และสามารถใช้ซ้ำโมดูลในหลายๆ จุดได้อย่างสะดวก

ในการนำเข้าโมดูลใน Python หรือ MicroPython โดยอ้างอิงจากโครงสร้างไฟล์ที่กล่าวถึงในตัวอย่างนี้ เราสามารถใช้วิธีการนำเข้า (import) ที่แตกต่างกันตามความต้องการ โดยที่การเลือกวิธีการนำเข้าขึ้นอยู่กับว่าเราต้องการเข้าถึงโมดูลจากไฟล์หรือแพ็กเกจไหนในโครงสร้างของโปรเจค วิธีการ import ที่จะกล่าวถึงมีหลายแบบดังนี้:

💓การนำเข้าโมดูลจากไฟล์ในโฟลเดอร์เดียวกัน : หากต้องการนำเข้าโมดูลจากไฟล์ในโฟลเดอร์เดียวกัน (เช่น จาก lib/mymodule.py ไปยัง main.py) สามารถใช้การนำเข้าทั่วไปโดยไม่ต้องระบุเส้นทางที่ซับซ้อน

  • ตัวอย่างจากไฟล์ main.py นำเข้า mymodule:
import lib.mymodule
  • ตัวอย่างหากต้องการใช้ฟังก์ชันหรือคลาสภายใน mymodule.py
from lib.mymodule import my_function

💓การนำเข้าโมดูลจากโฟลเดอร์ย่อยภายในแพ็กเกจ : หากต้องการนำเข้าโมดูลจากโฟลเดอร์ย่อยในแพ็กเกจ (เช่น จาก lib/helper/submodule1.py ไปยัง main.py), Python จะใช้ไฟล์ __init__.py เพื่อทำให้โฟลเดอร์นั้นกลายเป็นแพ็กเกจได้อย่างสะดวก

  • ตัวอย่างจากไฟล์ main.py นำเข้า submodule1 จากโฟลเดอร์ helper
import lib.helper.submodule1
  • ตัวอย่างหากต้องการใช้ฟังก์ชันหรือคลาสภายใน submodule1.py
from lib.helper.submodule1 import some_function

💓การใช้ไฟล์ __init__.py สำหรับการนำเข้าจากแพ็กเกจ : หากไฟล์ __init__.py ในโฟลเดอร์ถูกใช้ (แม้ว่าไฟล์นั้นจะว่างเปล่า) เพื่อบ่งบอกว่าโฟลเดอร์นั้นเป็นแพ็กเกจ สามารถนำเข้าโมดูลจากแพ็กเกจได้ง่ายๆ โดยไม่ต้องระบุเส้นทางยาว

ตัวอย่างหากไฟล์ __init__.py อยู่ในโฟลเดอร์ lib/ สามารถนำเข้าโมดูลจาก lib/ ได้ง่าย ๆ :

from lib import mymodule

💓การนำเข้าโมดูลจากไฟล์คอนฟิก (config.py) : หากคุณต้องการนำเข้าไฟล์คอนฟิก config.py ในโปรเจคเพื่อใช้งานค่าคอนฟิกต่างๆ ที่เก็บไว้ในไฟล์นี้, คุณสามารถทำได้โดยการใช้การนำเข้าทั่วไป

  • ตัวอย่างจาก main.py นำเข้าไฟล์ config.py
import config
  • ตัวอย่างหากต้องการใช้ค่าคอนฟิกบางตัวจาก config.py
from config import SOME_CONFIG_VALUE

💓 การนำเข้าทดสอบ (สำหรับการทดสอบ)

หากคุณกำลังทำการทดสอบในโฟลเดอร์ tests/ และต้องการนำเข้าไฟล์ทดสอบจากโฟลเดอร์นี้, สามารถทำได้ง่ายๆ เหมือนกันโดยการใช้ import ที่ระบุชื่อไฟล์ทดสอบ (เช่น test_mymodule.py)

ตัวอย่าง:

  • จากไฟล์ main.py หรือไฟล์อื่นๆ นำเข้าโมดูลทดสอบ test_mymodule
import tests.test_mymodule
  • หากจะเลือกนำเข้าฟังก์ชันจากไฟล์ทดสอบ
from tests.test_mymodule import test_some_function

💓💓 การนำเข้าแบบอ้างอิงจากโมดูล (Relative Imports) : ถือเป็นการนำเข้าที่อ้างอิงจากตำแหน่งของไฟล์ปัจจุบัน เป็นวิธีที่ช่วยให้การจัดการโครงสร้างโปรเจคมีความยืดหยุ่นมากขึ้น โดยไม่ต้องระบุเส้นทางไฟล์ทั้งหมด ซึ่งการใช้ .. หรือ ../.. จะช่วยในการอ้างอิงตำแหน่งของไฟล์ที่ต้องการนำเข้าในโฟลเดอร์ที่อยู่สูงขึ้นไปตามลำดับ

ตัวอย่างการนำเข้าภายในโครงสร้างไฟล์ที่มีหลายชั้น

my_project/
├── lib/
│ ├── subfolder/
│ │ └── submodule.py # ฟังก์ชันบางอย่างในนี้
│ └── mymodule.py # ฟังก์ชัน some_function อยู่ในนี้
├── helpers/
│ └── subfolder/
│ └── submodule.py # เราต้องการนำเข้าฟังก์ชันจาก mymodule.py ที่นี่

การนำเข้าโมดูลในกรณีต่าง ๆ :

💌 การนำเข้าจากโฟลเดอร์เดียวกัน : หากต้องการนำเข้าฟังก์ชันจาก mymodule.py ใน lib/ จากไฟล์ที่อยู่ใน helpers/subfolder/submodule.py, ใช้การขึ้นไปหนึ่งระดับจาก subfolder/ เพื่อไปที่ helpers/ และจากนั้นจะสามารถนำเข้า mymodule.py จาก lib/ ได้:

# ในไฟล์ helpers/subfolder/submodule.py
from ..mymodule import some_function

.. หมายถึง “ขึ้นไปหนึ่งระดับ” จาก subfolder/ ไปที่ helpers/ ซึ่งสามารถนำเข้า mymodule.py จาก lib/ ได้

💌 การนำเข้าจากโฟลเดอร์ย่อยใน lib/: หากต้องการนำเข้าฟังก์ชันจากไฟล์ที่อยู่ใน lib/subfolder/ ซึ่งเป็นโฟลเดอร์ย่อยของ lib/, คุณต้องขึ้นไปจาก subfolder/ และไปยัง helpers/ แล้วจึงเข้าไปที่ lib/subfolder/:

# ในไฟล์ helpers/subfolder/submodule.py
from ..lib.subfolder.submodule import some_function

.. จะขึ้นไปจาก subfolder/ ไปที่ helpers/ แล้วจากนั้นเข้าไปที่ lib/subfolder/ เพื่อดึงฟังก์ชันจาก submodule.py ที่อยู่ใน lib/subfolder/

💌 การนำเข้าจากโฟลเดอร์ที่สูงขึ้นสองระดับ : หากต้องการนำเข้าฟังก์ชันจาก mymodule.py ที่อยู่ใน lib/, โดยขึ้นไปสองระดับจาก subfolder/ (ผ่าน helpers/ และ my_project/**), สามารถใช้ **../../` ดังนี้:

# ในไฟล์ helpers/subfolder/submodule.py
from ../..mymodule import some_function

../../ หมายถึงการขึ้นไปสองระดับจาก subfolder/ ไปยัง helpers/ และจากนั้นไปที่ lib/ ซึ่งเก็บไฟล์ mymodule.py

💌 การนำเข้าจากโฟลเดอร์ที่สูงขึ้นสามระดับและเข้าไปในโฟลเดอร์ย่อยของ lib/: กรณีที่ต้องการขึ้นไปสูงสุดสามระดับจาก subfolder/ ไปยัง my_project/ และจากนั้นเข้าไปใน lib/subfolder/, สามารถใช้ ../../../ ดังนี้:

# ในไฟล์ helpers/subfolder/submodule.py
from ../../../lib/subfolder.submodule import some_function

../../../ หมายถึงการขึ้นไปสามระดับจาก subfolder/ ไปที่ helpers/, จากนั้นขึ้นไปอีกระดับจนถึง my_project/ และจากนั้นเข้าไปที่ lib/subfolder/submodule.py เพื่อดึงฟังก์ชันจากนั้น

กล่าวโดยสรุป การใช้ relative import ใน Python ช่วยให้การนำเข้าโมดูลหรือไฟล์ในโครงสร้างไดเร็กทอรีที่มีหลายชั้นเป็นไปอย่างยืดหยุ่น โดยไม่ต้องใช้เส้นทางไฟล์ที่สมบูรณ์ (absolute path) ซึ่งช่วยให้โค้ดสามารถทำงานได้ง่ายในโครงสร้างโปรเจคที่ซับซ้อนและยืดหยุ่นต่อการปรับเปลี่ยนตำแหน่งไฟล์

หลักการทำงานของ relative import คือ การอ้างอิงเส้นทางไฟล์โดยอิงจากตำแหน่งของไฟล์ปัจจุบัน โดยใช้สัญลักษณ์ . และ ..:

  • . หมายถึงโฟลเดอร์เดียวกัน ซึ่งใช้ในการนำเข้าไฟล์จากโฟลเดอร์ที่อยู่ในระดับเดียวกันกับไฟล์ปัจจุบัน
  • .. หมายถึงการขึ้นไปหนึ่งระดับจากโฟลเดอร์ปัจจุบัน เพื่อเข้าถึงไฟล์ที่อยู่ในโฟลเดอร์ที่สูงขึ้น
  • การใช้ ../.. หรือ ../../.. ช่วยในการขึ้นไปหลายระดับจากตำแหน่งปัจจุบัน

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

การนำเข้าไฟล์จากโฟลเดอร์อื่น ๆ ภายในโปรเจคทำได้อย่างง่ายดายและไม่ซับซ้อน ด้วยการใช้สัญลักษณ์ . และ .. เพื่อบอก Python ว่าจะต้องเข้าไปในโฟลเดอร์ย่อยหรือขึ้นไปยังโฟลเดอร์ที่สูงขึ้น โดยไม่ต้องพึ่งพาเส้นทางไฟล์เต็มรูปแบบ

🚩🚩🚩 การใช้ Import ที่ผิดพลาด 🚩🚩🚩

การนำสิ่งอื่นใดเข้ามาย่อมมีข้อผิดพลาดได้เสมอ ในไพทอน การนำโมดูลอื่นเข้ามาโดยใช้ import เกิดขึ้นได้ในหลายรูปแบบเช่น :

🚩นำเข้าฟังก์ชันที่ไม่มีในโมดูล สามารถเกิดขึ้นได้เมื่อพยายามนำเข้าฟังก์ชันที่ไม่ได้มีอยู่ในโมดูลที่กำหนด เช่น ฟังก์ชัน mean ที่ไม่ได้มีอยู่ในโมดูล math จะทำให้เกิดข้อผิดพลาด ImportError หรือ AttributeError เนื่องจากฟังก์ชันไม่สามารถหาได้ในโมดูลนั้น

การแก้ไขคือ การตรวจสอบว่าโมดูลนั้นมีฟังก์ชันที่ต้องการหรือไม่ โดยใช้คำสั่ง dir() เพื่อตรวจสอบฟังก์ชันที่มีอยู่ในโมดูล เช่น หากใช้คำสั่ง dir(math) จะได้รายการของฟังก์ชันทั้งหมดในโมดูล math เพื่อตรวจสอบว่า mean มีอยู่ในโมดูลนั้นหรือไม่

import math
print(dir(math))

ถ้าฟังก์ชันไม่ได้อยู่ในโมดูลที่ใช้อยู่ ให้เปลี่ยนไปใช้ฟังก์ชันจากโมดูลที่เหมาะสม เช่น ในกรณีนี้สามารถใช้โมดูล statistics ซึ่งมีฟังก์ชัน mean ให้ใช้แทนการใช้ math ได้ โดยการนำเข้าเป็น from statistics import mean

from statistics import mean

🚩นำเข้าฟังก์ชันที่มีชื่อเดียวกันจากหลายโมดูล ใน Python อาจทำให้เกิดข้อผิดพลาดหากไม่ระบุแหล่งที่มาของฟังก์ชันอย่างชัดเจน ตัวอย่างเช่น ฟังก์ชัน sqrt ที่มีอยู่ทั้งในโมดูล math และ numpy หากไม่ได้ระบุแหล่งที่มาของฟังก์ชัน Python จะไม่สามารถระบุได้ว่าต้องการใช้ฟังก์ชันจากโมดูลไหน ซึ่งอาจทำให้เกิดข้อผิดพลาดหรือผลลัพธ์ที่ไม่คาดคิดได้

วิธีการแก้ไขปัญหานี้คือการใช้คำสั่ง as เพื่อเปลี่ยนชื่อของโมดูลเมื่อทำการนำเข้าฟังก์ชันจากหลายๆ โมดูล ช่วยให้สามารถระบุแหล่งที่มาของฟังก์ชันได้อย่างชัดเจน การใช้คำสั่ง as ทำให้สามารถเรียกฟังก์ชัน sqrt จากโมดูลต่างๆ ได้โดยไม่สับสน ตัวอย่างเช่น เมื่อมีการนำเข้า math และ numpy ในเวลาเดียวกัน สามารถใช้คำสั่ง as ดังนี้:

import math as m
import numpy as np

print(m.sqrt(16)) # ใช้ sqrt จาก math
print(np.sqrt(16)) # ใช้ sqrt จาก numpy

ในกรณีนี้จะเห็นได้ว่า m.sqrt จะใช้ฟังก์ชัน sqrt จากโมดูล math และ np.sqrt จะใช้ฟังก์ชัน sqrt จากโมดูล numpy ซึ่งทำให้สามารถเลือกใช้ฟังก์ชันจากแต่ละโมดูลได้อย่างชัดเจนและไม่มีความสับสนเกิดขึ้น

🚩 นำเข้าฟังก์ชันที่ไม่มีในเวอร์ชันของ Python ที่ใช้ อาจเกิดข้อผิดพลาดได้เมื่อฟังก์ชันบางตัวถูกเพิ่มเข้ามาในเวอร์ชันใหม่ๆ ของ Python และไม่สามารถใช้งานได้ในเวอร์ชันเก่ากว่า ตัวอย่างเช่น ฟังก์ชัน math.comb ที่ถูกเพิ่มเข้ามาใน Python 3.8 หากพยายามใช้งานฟังก์ชันนี้ในเวอร์ชัน Python ที่ต่ำกว่า 3.8 จะทำให้เกิดข้อผิดพลาด เช่น ImportError หรือ AttributeError เนื่องจากฟังก์ชันดังกล่าวไม่มีในเวอร์ชันที่ใช้อยู่

ในการแก้ไขปัญหานี้สามารถทำได้ 2 วิธีหลักๆ:

  • อัปเกรด Python: หากต้องการใช้งานฟังก์ชันที่ถูกเพิ่มเข้ามาในเวอร์ชันใหม่ ควรอัปเกรด Python เป็นเวอร์ชันที่รองรับฟังก์ชันนั้น เช่น การอัปเกรด Python เป็นเวอร์ชัน 3.8 หรือใหม่กว่า โดยใช้คำสั่ง python -m pip install --upgrade python
  • ใช้ฟังก์ชันทางเลือก: หากไม่สามารถอัปเกรด Python ได้ เนื่องจากข้อจำกัดบางอย่าง เช่น การใช้งานในระบบที่ไม่สามารถอัปเดตเวอร์ชันได้ สามารถใช้ฟังก์ชันทางเลือกที่ทำงานได้ในเวอร์ชัน Python เก่า เช่น การคำนวณค่า comb ด้วยวิธีการที่เขียนเองแทนการใช้ฟังก์ชัน math.comb ใน Python 3.8 และใหม่กว่า ตัวอย่างการเขียนฟังก์ชันคำนวณค่า comb แบบเอง:
def comb(n, k):
if k > n:
return 0
if k == 0 or k == n:
return 1
num = 1
denom = 1
for i in range(1, k+1):
num *= (n - (k - i))
denom *= i
return num // denom

วิธีนี้ช่วยให้สามารถคำนวณค่า comb ในเวอร์ชัน Python ที่ต่ำกว่า 3.8 ได้โดยไม่ต้องอัปเกรด Python

🚩นำเข้าฟังก์ชันจากโมดูลที่ไม่ได้ติดตั้ง ในเครื่องคอมพิวเตอร์จะเกิดข้อผิดพลาด ModuleNotFoundError ซึ่งจะเกิดขึ้นเมื่อพยายามใช้งานฟังก์ชันจากโมดูลที่ไม่ได้ติดตั้งในเครื่อง Python ตัวอย่างเช่น หากพยายามนำเข้าฟังก์ชันจาก numpy แต่โมดูล numpy ไม่ได้ติดตั้งในเครื่องจะทำให้เกิดข้อผิดพลาดนี้ขึ้น การแก้ไขคือการติดตั้งโมดูลที่ขาดหายไปโดยใช้คำสั่ง pip install ซึ่งเป็นเครื่องมือที่ใช้ติดตั้งแพ็กเกจใน Python ในกรณีนี้หากต้องการใช้ numpy ก็สามารถติดตั้งโดยการใช้คำสั่ง pip install numpy

🚩 นำเข้าฟังก์ชันที่มีชื่อเดียวกันจากหลายโมดูล โดยไม่ระบุแหล่งที่มาหรือใช้ชื่อที่แตกต่างกัน Python จะใช้ฟังก์ชันที่ถูกนำเข้าล่าสุดเป็นค่าเริ่มต้น ซึ่งอาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิด ตัวอย่างเช่น หากนำเข้าฟังก์ชัน sqrt จากทั้ง math และ numpy โดยไม่ระบุแหล่งที่มาจะทำให้ฟังก์ชัน sqrt จาก numpy ถูกใช้แทนที่จาก math ซึ่งอาจทำให้เกิดผลที่ไม่ตรงตามความต้องการ การแก้ไขเพื่อหลีกเลี่ยงปัญหานี้คือการใช้คำสั่ง as เพื่อเปลี่ยนชื่อให้แตกต่างกัน เช่น:

from math import sqrt as math_sqrt
from numpy import sqrt as np_sqrt

print(math_sqrt(16)) # ใช้ sqrt จาก math
print(np_sqrt(16)) # ใช้ sqrt จาก numpy

การใช้คำสั่ง as จะช่วยให้สามารถระบุได้ชัดเจนว่าใช้ฟังก์ชัน sqrt จากโมดูลใด โดยไม่ต้องกังวลเรื่องการทับซ้อนของชื่อฟังก์ชัน

🚩 นำเข้าฟังก์ชันจากโมดูลที่ไม่รองรับกับระบบปฏิบัติการที่ใช้อยู่ เช่นการพยายามใช้งาน win32api บนระบบปฏิบัติการ macOS หรือ Linux ซึ่งโมดูลนี้รองรับเฉพาะ Windows จะเกิดข้อผิดพลาด ModuleNotFoundError เนื่องจากโมดูลนี้ไม่สามารถใช้งานในระบบที่ไม่รองรับได้

การแก้ไขปัญหานี้สามารถทำได้โดย:

  • ตรวจสอบระบบปฏิบัติการ: ก่อนการใช้งานโมดูลที่อาจจำกัดในบางระบบปฏิบัติการ ควรตรวจสอบว่าโมดูลนั้นรองรับระบบที่ใช้อยู่หรือไม่ โดยสามารถใช้คำสั่ง platform.system() เพื่อดึงข้อมูลระบบปฏิบัติการปัจจุบันและตรวจสอบความเข้ากันได้ของโมดูล
import platform
print(platform.system()) # ตรวจสอบระบบปฏิบัติการ
  • เลือกใช้โมดูลที่รองรับระบบทั้งหมด: หากโมดูลที่ต้องการใช้ไม่รองรับบางระบบปฏิบัติการ ควรหาทางเลือกที่สามารถทำงานได้บนทุกระบบปฏิบัติการ เช่นใช้โมดูล os หรือ subprocess ที่รองรับการทำงานร่วมกับระบบต่างๆ หรือหากต้องการใช้ฟังก์ชันจาก Windows เพียงบางอย่าง อาจต้องใช้โค้ดที่จำกัดการใช้งานเฉพาะระบบ Windows เท่านั้น
import platform
if platform.system() == "Windows":
import win32api # ใช้เฉพาะบน Windows
else:
print("ไม่สามารถใช้งาน win32api บนระบบนี้ได้")

การตรวจสอบและเลือกใช้งานโมดูลที่รองรับได้กับทุกระบบปฏิบัติการ จะช่วยหลีกเลี่ยงปัญหาการนำเข้าฟังก์ชันที่ไม่รองรับและทำให้โปรแกรมสามารถทำงานได้อย่างถูกต้องบนระบบที่หลากหลาย

🚩นำเข้าฟังก์ชันที่มีชื่อคล้ายกันจากโมดูลต่างๆ เช่น ฟังก์ชัน rand หรือ randint ที่อาจมีในโมดูล random และ numpy.random ซึ่งอาจทำให้เกิดความสับสนในการใช้งานและนำไปสู่ข้อผิดพลาดในผลลัพธ์ การระบุแหล่งที่มาของฟังก์ชันให้ชัดเจนจะช่วยป้องกันไม่ให้เกิดความสับสนและข้อผิดพลาดเหล่านี้

การแก้ไขคือการระบุแหล่งที่มาของฟังก์ชันอย่างชัดเจนโดยใช้คำสั่ง as เพื่อกำหนดชื่อที่แตกต่างกันสำหรับฟังก์ชันที่มีชื่อคล้ายกันจากหลายโมดูล:

import random
from numpy import random as np_random

print(random.randint(1, 10)) # ใช้ random จากโมดูล random
print(np_random.randint(1, 10)) # ใช้ random จาก numpy

กรณีนี้ ฟังก์ชัน randint ที่มาจาก random จะถูกเรียกโดยใช้ random.randint และฟังก์ชัน randint ที่มาจาก numpy.random จะถูกเรียกโดยใช้ np_random.randint ซึ่งจะช่วยให้สามารถใช้งานฟังก์ชันจากแต่ละโมดูลได้อย่างชัดเจนโดยไม่สับสนกัน

🚩 นำเข้าฟังก์ชันที่ไม่ได้รับการสนับสนุนในบางเวอร์ชันของโมดูล เช่น ฟังก์ชัน isnan จากโมดูล statistics ซึ่งไม่มีในเวอร์ชันบางเวอร์ชันของโมดูลนั้น จะทำให้เกิดข้อผิดพลาดและไม่สามารถใช้งานฟังก์ชันได้ตามต้องการ

การแก้ไขสามารถทำได้หลายวิธี:

  • ตรวจสอบเวอร์ชันของโมดูล: ก่อนใช้งานฟังก์ชันในโมดูล ควรตรวจสอบว่าเวอร์ชันของโมดูลที่ติดตั้งรองรับฟังก์ชันที่ต้องการหรือไม่ หากไม่แน่ใจ สามารถดูเอกสารหรือใช้คำสั่ง help() เพื่อตรวจสอบความสามารถของโมดูลในเวอร์ชันที่ใช้งาน
  • เลือกฟังก์ชันทางเลือก: หากฟังก์ชันที่ต้องการไม่รองรับในเวอร์ชันที่ใช้งานอยู่ สามารถใช้ฟังก์ชันที่มีอยู่ในโมดูลอื่นๆ ที่มีฟังก์ชันที่ทำหน้าที่คล้ายกัน เช่น ใช้ math.isnan() จากโมดูล math แทน statistics.isnan() ที่อาจจะไม่มีในบางเวอร์ชันของโมดูล statistics
import math

# ใช้ math.isnan() แทน statistics.isnan() หากฟังก์ชันนั้นไม่รองรับ
print(math.isnan(float('nan'))) # จะให้ผลลัพธ์เป็น True

การเข้าใจข้อผิดพลาดและการเลือกใช้ฟังก์ชันที่เหมาะสมจากโมดูลที่รองรับจะช่วยให้การพัฒนาโปรแกรมเป็นไปได้อย่างราบรื่นและไม่เกิดปัญหาในระหว่างการใช้งานฟังก์ชันต่าง ๆ

“ Acceptance is onerous, rejection more so; to refute with reason demands transcendence of bias ”

“ Acceptatio onerosum est, recusatio gravius; negare ratione requirit praeiudicium transcendere ”

“ เลือกรับนับว่าไม่ง่าย เลือกไม่รับกลับยากกว่า ด้วยว่าปฏิเสธสิ่งใดหากอาศัยเหตุผล ย่อมต้องข้ามพ้นอคติแห่งตน ”

Naturvirtus

IV Ianuarius MMXXV