【Python time】บทช่วยสอน: จากการหน่วงเวลาด้วย sleep() สู่การวัดเวลาที่แม่นยำด้วย perf_counter()
หากต้องการเรียกใช้ Python ผ่าน Command Prompt หรือ PowerShell บนคอมพิวเตอร์ของคุณ คุณจำเป็นต้องดาวน์โหลดและติดตั้ง Python ก่อน
หากคุณยังไม่ได้ติดตั้ง กรุณาดูบทความ การติดตั้ง Python และการตั้งค่าสภาพแวดล้อมการพัฒนา เพื่อทำการติดตั้ง Python
สวัสดีครับ! ผมเป็นผู้เชี่ยวชาญที่เรียนรู้การเขียนโปรแกรมกับ AI และสร้างเว็บไซต์ 2 แห่งได้ในเดือนครึ่ง เมื่อไม่กี่เดือนก่อน ผมก็เป็นมือใหม่ที่ไม่มีความรู้ด้านการเขียนโปรแกรมเลยเหมือนกับทุกๆ คนครับ
ในระหว่างที่เรียน Python คุณเคยเห็นโค้ด `import time` บ่อยๆ ในโค้ดที่ AI สร้างขึ้นหรือในโค้ดตัวอย่างของคนอื่นไหมครับ? หลายคนอาจจะรู้สึกว่า "เจอ `time` อีกแล้ว มันทำอะไรได้กันแน่?" หรือ "แค่ใช้หยุดเวลาหรือวัดเวลาเท่านั้นเหรอ?"
ในบทความนี้ ผมจะมาอธิบายเกี่ยวกับโมดูล `time` โดยจะเล่าจากประสบการณ์ที่ผมเคยติดขัดมาก่อน เพื่อให้มือใหม่สามารถเข้าใจได้ง่ายๆ โดยเฉพาะอย่างยิ่ง เราจะเน้นไปที่วิธีการ "หยุดการทำงานชั่วคราว (sleep)" และ "การวัดเวลาที่ใช้ในการประมวลผลอย่างแม่นยำ" ซึ่งเป็นสิ่งจำเป็นอย่างยิ่งในการใช้งานจริง เช่น การสร้างเว็บไซต์หรือการเก็บข้อมูล เมื่อคุณอ่านบทความนี้จบ คุณจะสามารถใช้งานโมดูล `time` ได้อย่างคล่องแคล่วแน่นอนครับ!
1. ทำไมเราต้องจัดการกับ "เวลา"? - ความสำคัญของการรอ
คุณคิดว่าโปรแกรมยิ่งทำงานเร็วยิ่งดีใช่ไหมครับ? จริงๆ แล้ว ในบางสถานการณ์ "การตั้งใจรอ" กลับเป็นสิ่งสำคัญอย่างยิ่ง ผมจะขอยกตัวอย่าง 2 กรณีที่ผมเคยเจอตอนพัฒนาเว็บไซต์ครับ
กรณีที่ 1: การรวบรวมข้อมูลจากเว็บไซต์ (Web Scraping)
เมื่อเราพยายามรวบรวมข้อมูลจากเว็บไซต์โดยอัตโนมัติ โปรแกรมจะเข้าถึงหน้าต่างๆ ด้วยความเร็วที่มนุษย์ทำไม่ได้ ทำให้เกิดการเข้าถึงเซิร์ฟเวอร์ของเป้าหมายจำนวนมากในเวลาอันสั้น ซึ่งเป็นการสร้างภาระหนักให้กับเซิร์ฟเวอร์ เปรียบเสมือนการที่เรายกพวกไปที่ร้านค้าเพื่อก่อกวนการทำธุรกิจ ในกรณีที่เลวร้ายที่สุด เราอาจถูกบล็อกการเข้าถึงได้เลยครับ
เทคนิค "การรอ" จึงเข้ามามีบทบาทสำคัญ การใส่คำสั่งให้รอสักสองสามวินาทีหลังจากเข้าถึงหน้าเว็บหนึ่งหน้า จะช่วยลดภาระของเซิร์ฟเวอร์ได้อย่างมาก และทำให้การรวบรวมข้อมูลเป็นไปอย่างมีมารยาท
กรณีที่ 2: การใช้งาน API
ในการใช้บริการภายนอก (API) ที่ให้ข้อมูลสภาพอากาศหรือแผนที่ การจัดการ "เวลา" ก็เป็นสิ่งที่ขาดไม่ได้ API จำนวนมากมีการจำกัดจำนวนการใช้งาน (Rate Limit) เช่น "ไม่เกิน 60 ครั้งต่อนาที" หากเราส่งคำขอเกินขีดจำกัดนี้ อาจทำให้เราไม่สามารถใช้บริการได้ชั่วคราวหรือได้รับข้อผิดพลาดกลับมา
ในกรณีนี้ก็เช่นกัน การ "รอ" เป็นเวลาที่กำหนดหลังจากส่งคำขอแต่ละครั้ง จะช่วยให้เราสามารถใช้งานได้ภายในขีดจำกัดครับ
การกำหนด "เวลาหน่วง" ให้กับโปรแกรมโดยเจตนานี้เรียกว่า Sleep Process และวิธีที่ง่ายที่สุดในการทำเช่นนี้ใน Python คือการใช้ฟังก์ชัน `time.sleep()`
2. การรออย่างแม่นยำ! หยุดการทำงานชั่วคราวด้วย `time.sleep()`
`time.sleep()` เป็นฟังก์ชันที่เรียบง่ายมาก ใช้สำหรับหยุดการทำงานของโปรแกรมชั่วคราวตามจำนวนวินาทีที่กำหนด เรามาดูวิธีใช้งานเบื้องต้นกันครับ
โค้ดด้านล่างเป็นตัวอย่างง่ายๆ ที่จะแสดงข้อความ "เริ่มการทำงาน" และหลังจากนั้น 3 วินาที จะแสดงข้อความ "ผ่านไป 3 วินาที สิ้นสุดการทำงาน" ลองคัดลอกไปรันในเครื่องของคุณดูนะครับ คุณจะเข้าใจความรู้สึกของ "การรอ" ได้ทันที
# นำเข้าโมดูล time
import time
print("เริ่มการทำงาน")
# หยุดการทำงานเป็นเวลา 3 วินาที
time.sleep(3)
print("ผ่านไป 3 วินาที สิ้นสุดการทำงาน")
เรายังสามารถกำหนดค่าเป็นทศนิยมได้ เช่น 0.5 (0.5 วินาที) หรือ 0.1 (100 มิลลิวินาที) ซึ่งช่วยให้ควบคุมเวลาได้ละเอียดมากยิ่งขึ้น
import time
print("รอสักครู่ 0.5 วินาที")
# หยุดการทำงานเป็นเวลา 0.5 วินาที
time.sleep(0.5)
print("เสร็จสิ้น!")
เมื่อใช้ร่วมกับลูป เราสามารถสร้างโปรแกรมที่ทำงานบางอย่างเป็นระยะๆ ได้อย่างง่ายดาย ตัวอย่างเช่น โปรแกรมนับถอยหลังทุกๆ 1 วินาที
import time
print("เริ่มนับถอยหลัง!")
for i in range(5, 0, -1):
print(i)
# รอ 1 วินาที
time.sleep(1)
print("ปล่อยตัว!")
3. ฟังก์ชันนาฬิกาจับเวลา! วิธีวัดเวลาการประมวลผลอย่างแม่นยำ
อีกหนึ่งหน้าที่สำคัญของโมดูล `time` คือการวัดเวลาที่ใช้ในการประมวลผล การทำความเข้าใจว่าส่วนไหนของโค้ดที่ใช้เวลามากที่สุดคือขั้นตอนแรกในการปรับปรุงประสิทธิภาพ
เมื่อคุณรู้สึกว่า "การประมวลผลนี้ช้าจัง..." การวัดเวลาจริงๆ เพื่อหาจุดคอขวดเป็นสิ่งสำคัญ แทนที่จะตัดสินจากความรู้สึก
วิธีที่มือใหม่มักใช้: `time.time()`
วิธีพื้นฐานที่สุดในการวัดเวลาคือการเรียกใช้ `time.time()` ก่อนและหลังการประมวลผล แล้วนำค่ามาลบกัน `time.time()` จะคืนค่าเป็นจำนวนวินาทีที่ผ่านไปตั้งแต่ "UNIX epoch" ซึ่งเป็นเวลามาตรฐาน (1 มกราคม 1970, 00:00:00)
import time
# บันทึกเวลาก่อนเริ่มการประมวลผล
start_time = time.time()
# เขียนโค้ดที่ต้องการวัดเวลาที่นี่
# ตัวอย่างเช่น ใส่โค้ดให้รอ 1 วินาที
print("กำลังประมวลผล...")
time.sleep(1)
print("ประมวลผลเสร็จสิ้น")
# บันทึกเวลาหลังสิ้นสุดการประมวลผล
end_time = time.time()
# คำนวณและแสดงผลเวลาที่ใช้ไป
elapsed_time = end_time - start_time
print(f"เวลาที่ใช้ในการประมวลผล: {elapsed_time} วินาที")
【เรื่องเล่าความล้มเหลวของผม】 กับดักของ `time.time()`
จริงๆ แล้ว `time.time()` นั้นสะดวก แต่ก็มีกับดักสำคัญอยู่หนึ่งอย่าง นั่นคือ มันได้รับผลกระทบจากการเปลี่ยนแปลงเวลาของระบบคอมพิวเตอร์
ตอนที่ผมเริ่มเรียนใหม่ๆ ผมเคยใช้ `time.time()` วัดการประมวลผลที่ใช้เวลานาน แต่ระหว่างนั้น คอมพิวเตอร์ของผมได้ทำการปรับเวลาอัตโนมัติ (NTP sync) ทำให้ผลลัพธ์ที่ได้ผิดเพี้ยนไปจากเวลาจริงอย่างสิ้นเชิง ตัวอย่างเช่น หากเวลาระบบถูกปรับย้อนหลังไป 1 วินาทีระหว่างการทำงาน เวลาที่คำนวณได้ก็จะสั้นลง 1 วินาที ซึ่งทำให้การวัดผลไม่แม่นยำเลยใช่ไหมครับ?
ดังนั้นจึงมีวิธีการวัดเวลาที่น่าเชื่อถือกว่าเข้ามาแทนที่
วิธีที่มือโปรใช้: `time.perf_counter()`
`time.perf_counter()` (ย่อมาจาก performance counter) เป็นฟังก์ชันที่ออกแบบมาเพื่อการวัดค่าในระยะเวลาสั้นๆ เช่น การวัดเวลาประมวลผลโดยเฉพาะ ค่าที่ฟังก์ชันนี้คืนกลับมาไม่ใช่ "เวลา ณ ขณะนั้น" แต่เป็น เวลาที่เพิ่มขึ้นอย่างต่อเนื่องและมีความละเอียดสูงสุด ที่ระบบปฏิบัติการมีให้
คำว่า "เพิ่มขึ้นอย่างต่อเนื่อง" (monotonically increasing) คือหัวใจสำคัญ ซึ่งหมายความว่า แม้เวลาของระบบคอมพิวเตอร์จะเปลี่ยนไป ค่าของตัวนับนี้จะไม่ย้อนกลับ ดังนั้น แม้จะมีการปรับเวลาเกิดขึ้นระหว่างการประมวลผล ก็จะไม่ส่งผลกระทบต่อผลการวัด
แม้แต่ใน เอกสารอย่างเป็นทางการของ Python ก็ยังแนะนำให้ใช้ `perf_counter()` ในการวัดเวลาประมวลผล
วิธีใช้งานก็เหมือนกับ `time.time()` ทุกประการครับ
import time
# บันทึกค่าตัวนับก่อนเริ่มการประมวลผล
start_counter = time.perf_counter()
# เขียนโค้ดที่ต้องการวัดเวลาที่นี่
print("กำลังประมวลผล...")
total = 0
for i in range(10000000): # ตัวอย่างการทำงานที่ใช้เวลานาน
total += i
print("ประมวลผลเสร็จสิ้น")
# บันทึกค่าตัวนับหลังสิ้นสุดการประมวลผล
end_counter = time.perf_counter()
# คำนวณและแสดงผลเวลาที่ใช้ไป
elapsed_time = end_counter - start_counter
print(f"เวลาที่ใช้ในการประมวลผล: {elapsed_time} วินาที")
หากไม่มีเหตุผลพิเศษอะไร ให้จำไว้เสมอว่าเมื่อต้องการวัดเวลาประมวลผล ให้ใช้ `time.perf_counter()` รับรองว่าไม่ผิดพลาดแน่นอนครับ!
4. 【ประยุกต์】 สำหรับนักสร้างเว็บ! ตัวจับเวลา HTML ที่ทำงานได้จริง
เราได้เรียนรู้เกี่ยวกับโมดูล `time` ของ Python กันไปแล้ว ซึ่งมีประโยชน์อย่างมากสำหรับโปรแกรมที่ทำงานฝั่งเซิร์ฟเวอร์ (backend) แต่ในฐานะนักสร้างเว็บ เราก็อยากจะแสดงผลลัพธ์เหล่านั้นให้ผู้ใช้เห็นในรูปแบบของหน้าเว็บใช่ไหมครับ?
ดังนั้น เราลองนำแนวคิดของโมดูล `time` ใน Python มาประยุกต์ใช้สร้างตัวนับถอยหลังง่ายๆ ที่ทำงานบนเบราว์เซอร์ด้วย HTML และ JavaScript กันดูครับ เช่นเดียวกับที่ `time.sleep(1)` ใน Python คือคำสั่ง "รอ 1 วินาที" ใน JavaScript เราสามารถใช้คำสั่ง `setInterval()` เพื่อ "ทำบางอย่างทุกๆ 1 วินาที" ได้เช่นกัน
โค้ดด้านล่างนี้เป็นไฟล์ HTML ทั้งหมด ลองคัดลอกโค้ดนี้ไปบันทึกเป็นไฟล์ชื่อ `timer.html` แล้วเปิดด้วยเบราว์เซอร์ดูครับ เมื่อกดปุ่ม คุณจะเห็นตัวเลขบนหน้าจอลดลงทุกๆ 1 วินาที นี่แหละครับคือขั้นตอนแรกที่จะทำให้ผู้ใช้ได้สัมผัสกับ "สิ่งที่ทำงานได้จริง"!
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ตัวนับเวลาถอยหลัง</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #121212;
color: #e0e0e0;
font-family: sans-serif;
text-align: center;
}
#timer-display {
font-size: 8rem;
font-weight: bold;
color: #669df6;
}
button {
margin-top: 2rem;
padding: 1rem 2rem;
font-size: 1.5rem;
cursor: pointer;
border: 1px solid #5f6368;
background-color: transparent;
color: #8ab4f8;
border-radius: 5px;
}
button:disabled {
cursor: not-allowed;
opacity: 0.5;
}
</style>
</head>
<body>
<div>
<h1>ตัวนับถอยหลังด้วย JavaScript</h1>
<div id="timer-display">10</div>
<button id="start-btn">เริ่ม</button>
<p>ประยุกต์แนวคิดจาก time.sleep() ของ Python!</p>
</div>
<script>
// เขียนโค้ด script ที่นี่
const timerDisplay = document.getElementById('timer-display');
const startButton = document.getElementById('start-btn');
let timeLeft = 10;
let timerId = null;
function startTimer() {
// ปิดการใช้งานปุ่มเพื่อป้องกันการคลิกซ้ำ
startButton.disabled = true;
// ทำงานทุกๆ 1 วินาที (1000 มิลลิวินาที)
timerId = setInterval(() => {
timeLeft--;
timerDisplay.textContent = timeLeft;
if (timeLeft <= 0) {
clearInterval(timerId); // หยุดตัวจับเวลา
timerDisplay.textContent = "Go!";
}
}, 1000);
}
// เริ่มตัวจับเวลาเมื่อมีการคลิกปุ่มเริ่ม
startButton.addEventListener('click', startTimer);
</script>
</body>
</html>
5. สรุปข้อควรระวังในการใช้โมดูล `time`
สุดท้าย เรามาทบทวนประเด็นสำคัญในการใช้งานโมดูล `time` กันอีกครั้งครับ
- ใช้ `time.sleep()` สำหรับการหน่วงเวลา: ใช้เมื่อต้องการหน่วงเวลาการทำงานโดยเจตนา เพื่อเป็นมารยาทในการทำ Web Scraping หรือการใช้ API
- ใช้ `time.perf_counter()` สำหรับการวัดเวลา: ใช้เมื่อต้องการวัดประสิทธิภาพการทำงานของโปรแกรมอย่างแม่นยำ เนื่องจากไม่ได้รับผลกระทบจากการเปลี่ยนแปลงเวลาของระบบ จึงน่าเชื่อถือกว่า `time.time()`
- ความแม่นยำของ `sleep()` ไม่ได้สมบูรณ์แบบ: แม้จะใช้ `time.sleep(1)` แต่ขึ้นอยู่กับสถานะการทำงานอื่นๆ ของระบบปฏิบัติการ เวลาที่รอจริงอาจจะเป็น 1.002 วินาที หรือ 1.01 วินาที ไม่ใช่ 1.000 วินาทีเป๊ะๆ ควรจำไว้ว่ามันไม่เหมาะกับการควบคุมที่ต้องการความแม่นยำสูงมาก
บทสรุป
ในครั้งนี้ เราได้เรียนรู้วิธี "หน่วงเวลา" และ "วัดเวลา" การทำงานโดยใช้โมดูล `time` ของ Python แม้จะดูเป็นโมดูลที่ไม่โดดเด่น แต่เป็นเครื่องมือที่ทรงพลังและขาดไม่ได้ในการสร้างโปรแกรมที่ใช้งานได้จริง
โดยเฉพาะอย่างยิ่ง `time.sleep()` เป็นฟังก์ชันสำคัญที่แสดงถึงการคำนึงถึงผู้อื่น (เซิร์ฟเวอร์) ส่วน `time.perf_counter()` ก็เป็นการคำนึงถึงตัวเอง (การปรับปรุงประสิทธิภาพของโค้ด)
ลองนำสิ่งที่ได้เรียนรู้วันนี้ไปประยุกต์ใช้ในโปรแกรมของคุณดูนะครับ ผมเชื่อว่ามันจะช่วยเพิ่มขอบเขตของสิ่งที่คุณสามารถสร้างได้อย่างแน่นอน
ไปยังขั้นตอนต่อไป
หลังจากที่เชี่ยวชาญการจัดการเวลากับโมดูล `time` แล้ว ทำไมไม่ลองเรียนรู้เครื่องมือที่มีประโยชน์สำหรับการจัดการข้อมูลอย่างมีประสิทธิภาพดูล่ะครับ? Python ยังมีโครงสร้างข้อมูลที่ทรงพลังอีกมากมายที่มาพร้อมกับตัวติดตั้งมาตรฐาน
ในบทความถัดไป เราจะเน้นไปที่โมดูล `collections` และแนะนำฟีเจอร์ต่างๆ เช่น `defaultdict` และ `deque` ที่ทำให้ dictionary และ list สะดวกสบายยิ่งขึ้น การใช้งานสิ่งเหล่านี้ได้อย่างคล่องแคล่วจะช่วยให้คุณสามารถเขียนโค้ดที่ซับซ้อนและสวยงามยิ่งขึ้นได้ครับ!
» สรุปโครงสร้างข้อมูลที่มีประโยชน์ในโมดูล collections