Data Warehouse

การเรียกใช้มอดูล (Module)

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

มอดูลภายในตัวและมอดูลเสริมภายนอก

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

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

มอดูลเสริมเหล่านี้ต้องติดตั้งเพิ่มเติมเอาเองโดยโหลดตามเว็บ บางมอดูลแค่โหลดแล้วนำมาใส่ในโฟลเดอร์ก็สามารถใช้ได้ทันที แต่ก็มีอยู่หลายอันที่ต้องมีขั้นตอนในการติดตั้ง อย่างไรก็ตามสำหรับ ใครที่ใช้โปรแกรมแพ็กเกจอย่าง anaconda spyder จะมีมอดูลเสริมติดตัวอยู่แล้วเป็นจำนวนมาก สามารถเรียกใช้ได้ทันทีเช่นเดียวกับมอดูลภายในตัว มอดูลเสริมมักถูกเก็บรวมอยู่แยกจากมอดูลภายในตัว โดยจะรวมอยู่ในโฟลเดอร์เดียว เช่น สำหรับ anaconda python3.5 ใน mac แล้วมอดูลเสริมถูกเก็บอยู่ใน

Users/<ชื่อผู้ใช้>/anaconda/lib/python3.5/site-packages  

สำหรับใน windows จะเป็น

C:\Anaconda3\Lib\site-packages  

การเรียกใช้

คำสั่งที่ใช้ในการเรียกใช้มอดูลก็คือ import โดยพิมพ์ import แล้วตามด้วยชื่อมอดูลที่ต้องการเรียกใช้ เช่น เรียกใช้มอดูลที่ชื่อ math ซึ่งเป็นมอดูลที่เก็บฟังก์ชันที่เกี่ยวกับการคำนวณทางคณิตศาสตร์

import  math

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

math.sin(1.57)  # ได้ 0.9999996829318346

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

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

ดังนั้นทำให้การใช้งานในระดับพื้นฐานอาจไม่จำเป็นต้อง import อะไรเลย แต่พอต้องการใช้งานในระดับเฉพาะทางขึ้นมาหน่อยจึงจะเรียกใช้ หากลองเปรียบเทียบกับภาษาอื่นๆก็จะเห็นทั้งที่ใช้ include และ import เช่นใน php ใช้ include ใน javascript ใช้ import ความหมายเหมือนกันเพียงแต่ในแต่ละภาษาใช้ต่างกันเท่านั้น

ในภาษาไพธอนมอดูลก็ถือว่าเป็นออบเจ็กต์ชนิดหนึ่ง คือออบเจ็กต์ชนิดมอดูล ลองใช้ฟังก์ชัน type เพื่อหาชนิดดูได้

print(type(math))  # ได้ <class 'module'>

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

print(type(math.sin))  # <class 'builtin_function_or_method'>

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

print(math.pi)  # ได้ 3.141592653589793

pi นี้คือค่า π เป็นแค่ค่าคงที่ตัวหนึ่ง หากหาชนิดก็จะได้ว่าเป็น float

การลบมอดูล

หากไม่ต้องการใช้มอดูลไหนแล้วก็อาจลบทิ้งโดยใช้คำสั่ง del จากนั้นก็จะไม่สามารถใช้มอดูลนั้นได้อีกจนกว่าจำเรียกใช้ใหม่

del  math  
math.sin(1.57)  # ได้ NameError: name 'math' is not defined

หรืออาจลบแค่ฟังก์ชันในมอดูลนั้นตัวเดียว เช่น

del  math.sin

แบบนี้ก็จะใช้ไม่ได้แค่ math.sin ส่วนฟังก์ชันอื่นเช่น math.cos ก็ยังใช้ได้อยู่

การละชื่อมอดูล

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

from  math  import  *

เท่านี้ก็สามารถใช้ฟังก์ชันทั้งหมดในมอดูล math ได้โดยไม่จำเป็นต้องมี math. นำหน้าแล้ว เช่นพิมพ์แค่ sin(1.57) ง่ายๆสั้นๆ ดอกจันทน์ * ในที่นี้มีความหมายว่าเรียกใช้ทุกสิ่งทุกอย่างที่อยู่ภายในมอดูลนั้นออกมา ให้หมด แต่ว่าถ้าหากอยากเรียกใช้แค่บางอันก็ทำได้โดยแค่เปลี่ยน * เป็นชื่อของสิ่งที่ต้องการใช้ เช่น

form  math  import  sin

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

การย่อชื่อมอดูลและฟังก์ชัน

หากชื่อยาวๆก็สามารถย่อลงได้ บ่อยครั้งที่ชุดคำสั่งยาวๆมักถูกย่อชื่อลง การย่อชื่อสามารถทำได้โดยเขียน as ต่อท้ายตามด้วยชื่อที่ต้องการย่อ เช่น

import  random  as  rd

จากนั้นเวลาเรียกใช้มอดูลนี้ก็พิมพ์แค่ชื่อที่ถูกย่อแล้วตามด้วยชื่อฟังก์ชัน เช่น

rd.uniform(0,100)  # จะได้ค่าเลขสุ่มซึ่งอยู่ในช่วง 0 ถึง 100

มอดูล random นี้เป็นมอดูลที่ประกอบด้วยฟังก์ชันที่เกี่ยวข้องกับการสุ่ม (รายละเอียดเกี่ยวมอดูล random อ่านได้ใน https://phyblas.hinaboshi.com/20160508) ฟังก์ชัน uniform นั้นจะคืนค่าเป็นจำนวนจริงสุ่มภายในช่วงระหว่างอาร์กิวเมนต์ ๒ ตัวที่ใส่ลงไป สิ่งที่ย่อชื่อได้นั้นไม่เพียงแต่ชื่อมอดูลเท่านั้น แต่ชื่อฟังก์ชันภายในมอดูลก็สามารถย่อได้เช่นกัน เช่น

from  random  import  uniform  as  unif

เพียงเท่านี้ก็สามารถใช้ random.uniform ได้โดยพิมพ์แค่ unif(0,100) ชื่อมอดูลที่ถูกเรียกขึ้นมานั้นมีสถานะเหมือนตัวแปรตัวหนึ่ง จะใช้ชื่อนี้เป็นตัวแปรทำอย่างอื่นอีกไม่ได้ ถ้าใช้จะถูกเขียนทับ เช่น

import  math  
math = 100  
math.sin(1.57)  # ได้ AttributeError: 'int' object has no attribute 'sin'

นั่นเพราะพอแทนค่าลงไปแบบนี้กลายเป็นว่าตัวแปรที่ชื่อ math ไปรับค่า 100 แล้วมันก็จะไม่ใช่เป็นมอดูลที่ชื่อ math อีกต่อไป กลายเป็นค่าจำนวนเต็มธรรมดา เราจะใช้มันไม่ได้อีกจนกว่าจะ import ใหม่ หรือแม้แต่เวลาที่ใช้ชื่อย่อก็เช่นกัน ต้องระวังไม่ให้ชื่อย่อนั้นไปซ้ำกับชื่อตัวแปรที่ต้องการจะใช้

มอดูลย่อยภายในมอดูลหลัก

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

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

ตัวอย่างการใช้มอดูล os

import  os  
print(os.uname())  
print(os.getcwd())  
print(os.path.abspath('untitled0.py'))

จะเห็นว่าเมื่อเรียกใช้มอดูล os โดย import เฉยๆเวลาที่ใช้ os.path.abspath จะต้องพิมพ์ยาวไล่ตั้งแต่ชื่อมอดูลหลัก ตามด้วยชื่อมอดูลย่อย แล้วจึงจะเป็นชื่อฟังก์ชัน อย่างไรก็ตามสามารถย่อได้โดยใช้ from

from  os  import  path

แบบนี้ก็จะสามารถละ os. ด้านหน้าไปได้ path.abspath หรือย่อกว่านั้นคือละชื่อมอดูลย่อยไปด้วย

from  os.path  import  *

หรือ

from  os.path  import  abspath

แบบนี้ก็จะใช้ฟังก์ชัน abspath ได้โดยพิมพ์แค่ชื่อ abspath มอดูลย่อยก็สามารถทำการย่อชื่อได้ เช่น

from  os  import  path  as  op

หรือ

import  os.path  as  op

สองแบบนี้มีค่าเท่ากัน คือเวลาเรียกใช้ abspath จะต้องพิมพ์เป็น op.abspath

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

คลาสที่มากับมอดูล

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

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

ตัวอย่างการใช้

import  fractions  
a = fractions.Fraction(1,3)  
print(a)  # ได้ 1/3  
print(type(a))  # ได้ชนิดของข้อมูลเป็น <class 'fractions.Fraction'>

ในที่นี้จะเห็นว่าตัวแปร a กลายเป็นออบเจ็กต์ชนิดใหม่ ซึ่งก็คือชนิด fractions.Fraction ชื่ออาจจะดูยาวไปสักหน่อยเพราะต้องขึ้นต้นด้วยชื่อมอดูลก่อน รงส่วน a = fractions.Fraction(1,3) นั้นเป็นการประกาศตัวแปรและใส่ค่าให้ โดยเวลาสร้างข้อมูลชนิดนี้ทำได้หลายวิธี แต่ในที่นี้ใช้วิธีที่ง่ายที่สุดก็คือใส่อาร์กิวเมนต์เป็นเลขเศษตามด้วยเลขส่วน มองดูแล้วอาจรู้สึกว่ายุ่งยากต้องพิมพ์ตั้งยาวขนาดนี้เพื่อแค่ให้ได้จำนวนเศษส่วนมาตัวหนึ่ง อย่างไรก็ตามสามารถย่อได้ เช่น

from  fractions  import  Fraction  as  fr  
a = fr(1,3)

แบบนี้จะดูง่ายขึ้นเยอะ เพียงแต่ว่าเวลาถามถึงชื่อชนิดข้อมูลโดย print(type(a)) ก็ยังจะได้ผลเป็น <class 'fractions.Fraction'> เหมือนเดิม เพราะในที่นีคำว่า fr แค่มาแทน fractions.Fraction เพื่อความสะดวกเท่านั้น ข้อมูลเลขเศษส่วนเวลาที่สั่ง print จะแสดงผลออกมาเป็นเลขเศษส่วน คือเป็นเลขเศษคั่นด้วย / แล้วตามด้วยเลขส่วน นี่เป็นตัวอย่างคร่าวๆของการสร้างและใช้ออบเจ็กต์ชนิดใหม่ ที่จริงแล้วยังมีออบเจ็กต์อีกจำนวนมากมายหลายชนิดซึ่งจะต้องได้เจอต่อไปอีก เยอะ

ตัวอย่างมอดูลอื่นๆ

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

  • time ทำหน้าที่จัดการเกี่ยวกับเวลา
  • datetime ยามออบเจ็กต์ชนิดวันเวลาขึ้นมา ช่วยให้การคำนวณอะไรที่เกี่ยวกับวันเวลาเป็นไปได้อย่างสะดวก
  • decimal นิยามเลขทศนิยมฐานสิบซึ่งมีความแม่นยำสูงกว่าเลขทศนิยมตามที่ต้องการ
  • sys เก็บตัวแปรต่างๆที่ควบคุมจัดการระบบ
  • io จัดการกับการรับเข้าและนำออกข้อมูล
  • re สำหรับใช้เอ็กซ์เพรชชันแบบปกติ
  • platform ให้ข้อมูลเกี่ยวกับแพล็ตฟอร์มที่ใช้อยู่
  • zipfile สำหรับจัดการไฟล์ zip เช่นอัดรวมไฟล์และแตกไฟล์

นอกจากนี้ขอยกตัวอย่างมอดูลเสริมภายนอกที่เป็นที่นิยมจำนวนหนึ่งด้วย

  • numpy นิยามออบเจ็กต์ชนิดแถวอาเรย์ที่สามารถคำนวณเมทริกซ์ ใช้ในการคำนวณได้อย่างมีประสิทธิภาพ
  • scipy เครื่องมือสำคัญที่ใช้ในการคำนวณทางวิทยาศาสตร์
  • matplotlib สำหรับวาดกราฟ
  • pandas สำหรับจัดการกับข้อมูลอย่างเป็นระบบในรูปแบบตาราง
  • mayavi สำหรับวาดกราฟสามมิติ
  • py2exe สำหรับเปลี่ยนโปรแกรมภาษาไพธอนให้เป็น .exe
  • xlrd สำหรับจัดการไฟล์ excel
  • PyQt / PySide สำหรับสร้างกราฟิกยูเซอร์อินเทอร์เฟซ (GUI)
  • OpenGL สำหรับแสดงผลภาพกราฟิก

ใน IDE ที่เป็นแพ็กเกจเช่น anaconda spyder นั้นจะมีมอดูลเสริมอยู่หลายตัวติดมาตอนลงอยู่แล้ว แต่มอดูลไหนที่ไม่มีก็สามารถติดตั้งเพิ่มได้อย่างไม่ยาก

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

สรุปเนื้อหา

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

อ้างอิง

http://www.tohoho-web.com/python/module.html
http://docs.python.jp/3/tutorial/modules.html
http://docs.python.jp/3/library/index.html

Reference : https://phyblas.hinaboshi.com/tsuchinoko15