หลาย ๆ คนที่ศึกษา กำลังศึกษาหรือเรียนไพธอน มักจะเจอโค้ด if __name__ == '__main__': นี้กันอยู่เป็นประจำ ซึ่งทำให้เราปวดหัวและยังงงงวยกับเจ้าโค้ดนี้ และแน่นอนว่าหลายคนยังไม่รู้ว่า โค้ดภาษาไพธอนตรงส่วนนี้ทำหน้าที่อะไรกันแน่ และบทความสอนเกี่ยวกับเรื่องนี้ในไทยยังมีค่อนข้างน้อยไปจนถึงน้อยมาก เลยขอเขียนบทความนี้ขึ้นมา หวังว่าคงเป็นประโยชน์ไม่มากก็น้อย และในบทความนี้จะมาไขข้อข้องใจให้หายสงสัยกันครับ
จุดประสงค์
- เข้าใจและสามารถใช้งาน if __name__=='__main__': ได้เป็นอย่างดี
- เข้าใจเกี่ยวกับ Python Module พื้นฐานและการอิมพอร์ตโมดูลเข้ามาใช้งาน
- เข้าใจเกี่ยวกับ Python Package และ __init__.py ไฟล์
Python Module และ Package คืออะไร
ก่อนที่จะเข้าสู่ไฮไลท์ของเรื่อง จะขออธิบายเกี่ยวกับโมดูลและแพ็คเกจในภาษาไพธอนกันก่อนแบบภาพรวม จริง ๆ แล้ว Module ก็คือ Python File ส่วน Package ก็คือ Folder ที่เก็บโมดูลนั่นเอง โดยแพ็คเกจนั้นจะมีไฟล์ __init__.py กำหนดไว้ในโฟลเดอร์นั้น ๆ เพื่อบ่งบอกว่าโฟลเดอร์นี้คือ Package นั่นเอง โดยการเข้าถึง module และ package ก็จะขึ้นอยู่กับลำดับชั้นไฟล์และแพ็คเกจนั้น ๆ (File and Package Hierarchy) ขอยกตัวอย่างดังภาพด้านล่าง
(Photo: Basic Python คอร์ส - STACKPYTHON)
และด้านล่างคือการจัดวางไฟล์และการอิมพอร์ตในรูปแบบต่าง ๆ ตามระดับของโมดูลและแพ็คเกจที่ถูกจัดวางไว้ในโปรเจคท์
การจัดวาง Package และ Module (Photo: Basic Python คอร์ส - STACKPYTHON)
"ในบทความนี้จะไม่ได้อธิบาย Module และ Package แบบละเอียดยิบ โดยจะแยกเป็นบทความในเรื่องนี้แบบเฉพาะและจะเขียนในบทความต่อ ๆ ไป แต่ก็เชื่อว่าในบทความนี้ หลาย ๆ คนคงจะเข้าใจเกี่ยวกับโมดูลและแพ็คเกจได้เป็นอย่างดีและมองภาพรวมกันออกในเบื้องต้นกันครับ"
Python Module Exercises
ลองมาทดสอบเขียนโค้ดอีกสักหน่อยเพื่อทำความเข้าใจเพิ่มเติม
tutorial1.py
#tutorial.py print("Hello, this is a Python tutorial from STACKPYTHON")ซึ่งจากโค้ดด้านบนนั้นมีการตั้งชื่อไฟล์ว่า tutorial.py และก็ไม่ได้มีคำสั่งอะไรมากมาย แสดงผลเพียงแค่คำสั่ง print("Hello, this is a Python tutorial from STACKPYTHON") ซึ่งเจ้าไฟล์นี้แหละก็คือโมดูลอย่างสมบูรณ์แบบด้วยตัวมันเอง ถึงตรงนี้หลายคนเริ่มพอจะมองภาพออกแล้ว ดังนั้นขอเสริมให้ชัดขึ้นไปอีก
ทำการสร้างไฟล์ขึ้นมาอีกหนึ่งไฟล์ที่มีชื่อว่า tutorial2.py
tutorial2.py
import tutorial1 print("Hello, This is the second Python file, tutorial2")จากโค้ดด้านบนในไฟล์ tutorial2.py นั้นจะสามารถเรียกได้ว่า ได้ทำการอิมพอร์ตโมดูล tutorial1 มาใช้งาน ซึ่งการเรียกโมดูลนี้จะไม่ได้เรียก .py ซึ่งเป็นนามสกุลของไฟล์เข้าเกี่ยวข้องแล้ว และเมื่อรันโค้ดจะได้ผลลัพธ์ดังนี้
Output
ซึ่ง Hello ในบรรทัดแรกจะเป็นผลลัพธ์มาจากคำสั่ง print("Hello, this is a Python tutorial from STACKPYTHON") ในโมดูล tutorial1 ซึ่งจะถูกรันก่อน เพราะได้ถูกอิมพอร์ตเข้ามาในส่วนบนสุดของไฟล์ tutorial2.py ซึ่งแน่นอนว่าโดยปกติแล้วโค้ดนั้นจะทำงานตามลำดับ จากซ้ายไปขวาและบนลงมาล่างเสมอ
สรุปได้ว่า
- Python Module คือ Python File
- Python Package คือ Folder ที่เก็บไพธอนโมดูลอีกที
- __init__.py คือไฟล์เปล่า ๆ ที่บ่งบอกว่าโฟลเดอร์นี้คือ Package
- ถ้าจะเรียกไฟล์ให้เรียกว่า tutorial1.py, tutorial2.py
- ถ้าจะเรียกเป็นโมดูลให้ตัด .py ออกเหลือแค่ชื่อ ก็จะได้เป็นโมดูลชื่อว่า tutorial1 และ tutorial2
- โค้ดทำงานตามลำดับจากซ้ายไปขวา บนลงมาล่าง
if __name__=='__main__': คืออะไร ?
หลังจากทำความเข้าใจพื้นฐานก่อนหน้าที่ควรรู้ก็คือโมดูลมาเรียบร้อยแล้ว คราวนี้ก็ถึงไฮไลท์สำคัญของบทความนี้กันแล้ว โดยปกตินั้น __name__ จะเปรียบเสมือนตัวแปรตัวแปรหนึ่งของไพธอน แต่เป็นตัวแปรชนิดพิเศษสังเกตได้อีกทางหนึ่งคือมี Dunder หรือ เครื่องหมาย Double Underscores (Name Mangling) อยู่ในเมธอดหรือตัวแปรนั้น เช่น Dunder Methods เหล่านี้ __init__() , __str__() etc
โดยปกติถ้าเราทำการรันไพธอนไฟล์ไหน ไฟล์นั้นก็จะหลายเป็น main ไฟล์ หรือ '__main__' โดยอัตโนมัติ
แต่เดี๋ยวก่อน อย่าเพิ่งเชื่อถ้ายังไม่ได้พิสูจน์ โดยอันดับแรกให้ทำการสร้างไฟล์ขึ้นมาสองไฟล์มีชื่อว่า mod1.py และ mod2.py
mod1.py
# mod1.py def func1(): print(f"This is mod1.py, and it is {__name__} module") func1()สังเกตตรง {__name__} จะเห็นว่านี่ก็คือตัวแปร ๆ หนึ่งเพราะชัดเจนว่าสามารถอยู่ใน { } ซึ่งเป็นไวยากรณ์ของไพธอนในรูปแบบของ f-string ที่เราสามารถแสดงผลข้อความโดยใช้ฟังก์ชัน print() ตามปกติ เพียงแต่ว่ารูปแบบนี้จะทำให้สะดวกและอ่านง่ายกว่า (ในความคิดเห็นของผู้เขียน) จากนั้นทำการรันโค้ดตามปกติ
Output
This is mod1.py, and it is __main__ moduleสังเกตผลลัพธ์หลังจากรันโค้ดเสร็จ จะพบว่าโมดูลนี้คือ main module ถูกต้องตามที่ได้อธิบายไปก่อนหน้านี้
จากนั้นทำการสร้างไฟล์ที่สองขึ้นมาที่มีชื่อว่า mod2.py
mod2.py
import mod1 def func2(): print(f"This is mod2.py, and it is {__name__} module") func2()โดยในไฟล์ mod2.py นี้จะมีการอิมพอร์ต import mod1 โมดูลเข้ามาใช้งานด้วย จากนั้นให้ทำการรันโค้ด
Output
This is mod1.py, and it is mod1 module This is mod2.py, and it is __main__ moduleอธิบายสั้น ๆ แบบกระชับได้ดังนี้
- ผลลัพธ์ในบรรทัดแรก This is mod1.py, and it is mod1 module นั้นคือผลลัพธ์จากโมดูล mod1 จากฟังก์ชัน func1() นั่นเองและชัดเจนว่าไม่ได้เป็น main โมดูล
- ผลลัพธ์ในบรรทัดที่สอง This is mod2.py, and it is __main__ module และก็ชัดเจนว่าในส่วนของโมดูล mod2 นั้นก็คือ main module ก็เพราะว่ากำลังถูกรันอยู่นั่นเอง
ทดสอบใช้ if __name == '__main__':
ให้ทำการแก้ไขไฟล์ทั้ง 2 ไฟล์ โดยเปลี่ยนแปลงโค้ดดังนี้
mod1.py
def func1(): print(f"This is mod1.py, and it is {__name__} module") def second_func_mod1(): print(f"This is the second function from mod1.py") func1() second_func_mod1()ในส่วนของ mod1.py ไฟล์หรือสคริปต์นี้จะเป็นการรันและมีการประกาศและเรียกใช้งานฟังก์ชันทำงานตามปกติ ซึ่งก็จะได้ผลลัพธ์ตามปกติ
Output 1
This is mod1.py, and it is __main__ module This is the second function from mod1.pymod2.py
import mod1 print("Running code on mod2 now") def func2(): print(f"This is mod2.py, and it is {__name__} module") def second_func_mod2(): print(f"This is the second function from mod2.py") if __name__ == '__main__': passในส่วนของ mod2.py จะมีการกำหนดเงื่อนไขเพิ่มเติมคือถ้าในไฟล์นี้คือ main ไฟล์ให้ทำการรัน Statement หรือโค้ดด้านล่าง ซึ่งสามารถกำหนดว่าจะให้ทำอะไรได้ตามต้องการ โดยในโค้ดนี้จะยังไม่ทำอะไร โดยทำการใส่ pass ซึ่งเป็น Python Keyword อีกตัวหนึ่งที่ใส่ไว้เพื่อไม่ให้โค้ดในส่วนนี้ว่าง ป้องกัน error ทำให้โปรแกรมยังทำงานต่อไปได้นั่นเอง
โดยโค้ดด้านบนจะได้ผลลัพธ์คล้ายคลึงกันกับ mod1.py เพียงแต่ว่า นอกจากเพิ่มเงื่อนไขให้รันถ้าไฟล์นี้เป็น main ไฟล์แล้วนั้น ก็ได้ทำการเพิ่มโค้ดเข้าอีกหนึ่งส่วนอยู่ด้านบนนั่นก็คือ print("Running...") และเมื่อสังเกตผลลัพธ์ก็จะพบว่า ได้มี Output เพิ่มมาอีก 1 แถว นั่นก็คือ Running code on mod2 now ต่อท้ายจากผลลัพธ์ของโค้ดที่อิมพอร์ตเข้ามาจาก import mod1
Output 2
This is mod1.py, and it is mod1 module # mod1 This is the second function from mod1.py # mod1 Running code on mod2 now # mod2if else คือ Control Statement ซึ่งเมื่ออ่านถึงตรงนี้คงเข้าใจกันดีแล้ว ดังนั้นเราสามารถที่จะควบคุม Statement หรือการกระทำต่าง ๆ ได้ตามต้องการ โดยสังเกตได้ดังโค้ดในโค้ดด้านล่างและสามารถปรับเปลี่ยนได้ตามต้องการ
mod2.py
import mod1 print("Running code on mod2 now") def func2(): print(f"This is mod2.py, and it is {__name__} module") def second_func_mod2(): print(f"This is the second function from mod2.py") # New if __name__ == '__main__': print('') func2() second_func_mod2() else: passOutput 3
This is mod1.py, and it is mod1 module This is the second function from mod1.py Running code on mod2 now This is mod2.py, and it is __main__ module This is the second function from mod2.pyสร้างไฟล์ขึ้นมาใหม่ซึ่งเป็นไฟล์สุดท้ายชื่อว่า main.py main.py และทำการอิมพอร์ตทั้ง mod1 และ mod2 เข้ามาใช้งาน
main.py
import mod2 import mod1 print('') print("Hope you enjoy reading this article")และเมื่อสังเกตที่ผลลัพธ์นั้นในส่วนของฟังก์ชัน print('') , func1() และ
second_func_mod2() จะไม่ทำงานเนื่องจากว่าฟังก์ชันเหล่านี้กำหนดให้ทำงานในไฟล์ mod2.py เท่านั้น ซึ่งจะรันก็ต่อเมื่อเป็น main ไฟล์ แต่ตอนนี้ main คือ main.py ซึ่งได้ถูกสร้างขึ้นมาใหม่ล่าสุด
Output 4
This is mod1.py, and it is mod1 module This is the second function from mod1.py Running code on mod2 now Hope you enjoy reading this articleReal use case from Flask framework
จะขอยกกรณีตัวอย่างที่หลายคนที่ได้ศึกษา Flask เฟรมเวิร์คมา โดยจะสังเกตเห็นได้ในโค้ดของ Flask ใน app.py จะมีโค้ดในส่วนของ if __name__ == '__main__': ติดมาด้วย โดยอยู่ในส่วนล่างสุดของไฟล์
app.py
from flask import Flask app = Flask(__name__) @app.route('/') def home(): return "Hello My First Flask Project" if __name__ == '__main__': app.run(debug=True)สรุปด้านบนแบบรวบรัดกระชับสุด ๆ เลยก็คือถ้าไฟล์ที่กำลังรันอยู่ตอนนี้คือ app.py และไฟล์นี้คือ main ไฟล์ ของเราเพราะว่าไฟล์กำลังถูกรันอยู่นั่นเอง และในฟังก์ชัน app.run() นี้ก็จะมีคำสั่งมากมายที่อยู่ใน Flask เมธอด run ซึ่งจะไว้ใช้รันเซิร์ฟเวอร์, ดีบั๊ก, ฯลฯ
บทความแนะนำสำหรับ Flask
- ใช้งาน Flask ร่วมกับ SQLAlchemy เพื่อเชื่อมต่อฐานข้อมูล SQLite
- ประยุกต์ใช้งาน Flask ร่วมกับ Chart.js
คอร์สเรียนแนะนำสำหรับ Flask
- Python Web Development with Flask - STACKPYTHON
สรุป
หลังจากอ่านจนจบบทความนี้แล้ว หวังเป็นอย่างยิ่งครับว่าจะทำให้หายข้องใจว่า if __name__ == '__main__' คืออะไร และยังมีเสริมเกี่ยวกับ Module และ Package เข้ามาด้วย เพราะว่าเป็นพื้นฐานก่อนหน้าที่ควรรู้ และขอสรุปไว้ดังนี้ครับ
- if __name__ == '__main__': ช่วยให้สามารถที่จะเลือกรันหรือไม่รันสคริปต์หรือไฟล์ที่ต้องการได้ ซึ่งมีประโยชน์เป็นอย่างมากเกี่ยวกับการรันและการอิมพอร์ตโมดูลต่าง ๆ เข้ามาใช้งาน
- โมดูล (Module) คือ Python ไฟล์
- แพ็คเกจ (Package) คือ Folder หรือ Directory ที่เก็บ Python โมดูล
- __init__.py เป็น Python ไฟล์ ที่บ่งบอกว่าโฟลเดอร์นี้คือแพ็คเกจ
- __name__ คือตัวแปรชนิดหนึ่งของภาษาไพธอน ที่ใช้บอกชื่อโมดูล
- __main__ คือ main ไฟล์ หรือไฟล์, สคริปต์ ที่กำลังรันโค้ดในขณะนั้น
- โปรแกรมจะทำงานจาก Top Level คือทำงานจากบนลงล่าง เป็นลำดับขั้น
หากมีข้อสงสัยหรือเสนอแนะเพิ่มเติมก็คอมเมนต์เข้ามาที่คอมเมนต์ด้านล่างได้เลยนะครับ สามารถทำการล็อกอินผ่าน Facebook เพื่อคอมเมนต์และกด response emotions ต่าง ๆ ได้เลยครับ พบกันกับบทความถัดไปครับ See ya next article
| Like | Comment | Share | >> STACKPYTHON
References
The following are really useful resources for making this article happen
[python.org] - Top Level Script Environment
[FreeCodeCamp] - Python if __name__ == __main__ Explained with Code Examples
[//bic-berkeley.github.io/] - Two double underscores
variables