6. มอดูล (Modules)
6.1 เพิ่มเติม (More on Modules)
6.2 มอดูลมาตรฐาน (Standard Modules)
6.3 ฟังก์ชัน dir() (The dir() Function)
6.4 แพกเกจ (Packages)
ธรรมชาติของการเขียนโปรแกรมแบบมือใหม่ (มือเก่าจะวางโครงสร้างก่อน) ก็คือหัดเขียนในแบบโต้ตอบก่อน ตามมาด้วยลงไฟล์จริง พอไฟล์ใหญ่ขึ้นก็ต้องอาศัยมอดูลเพื่อเอาไว้เก็บพวกฟังก์ชันที่ต้องเรียกใช้ซ้ำ ๆ กัน พูดง่าย ๆ คือมอดูลคือที่เก็บฟังก์ชันเพื่อให้เรียกใช้สะดวก
ใช้ประโยค import module_name ในการเรียกใช้
ชื่อมอดูลจะถูกเก็บไว้ในตัวแปรส่วนรวม (เฉพาะในมอดูล) ชื่อ __name__
สมมุติเราสร้างมอดูลเป็นไฟล์ชื่อ fibo.py (ให้อยู่ในไดเรกทอรีปัจจุบัน) มีเนื้อไฟล์ว่า
# Fibonacci numbers module
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while b < n:
print b,
a, b = b, a+b
def fib2(n): # return Fibonacci series up to n
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
return resultเริ่มต้นใช้งานว่า
>>> import fibo
เรียกใช้งานฟังก์ชันโดย
>>> fibo.fib(1000) 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 >>> fibo.fib2(100) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>> fibo.__name__ 'fibo'
เพื่อให้กระชับ เราสามารถกำหนดค่าตัวแปรแทนเมธอดในมอดูลได้
>>> fib = fibo.fib >>> fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377
6.1 เพิ่มเติม (More on Modules)
- สามารถตั้งให้มีโค้ดในการรันครั้งแรก (ครั้งเดียว) ที่ถูกอิมพอร์ตได้
- เนื่องจากต้องอ้างอิงค่าในมอดูลผ่านชื่อมอดูล ในรูปของ
modname.itemnameจึงไม่ต้องกังวลเรื่องชื่อตัวแปรในมอดูลจะซ้ำกับตัวแปรในโปรแกรมหลัก - มอดูลสามารถอิมพอร์ตมอดูลอื่นได้
- เขียนรูปแบบการอิมพอร์ตได้หลากหลาย
- เลือกอิมพอร์ตบางฟังก์ชัน
>>> from fibo import fib, fib2 >>> fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377
- อิมพอร์ตทุกฟังก์ชัน ที่ไม่ใช่ฟังก์ชันท้องถิ่น (ฟังก์ชันท้องถิ่นจะถูกนำหน้าชื่อฟังก์ชันด้วยสัญลักษณ์ขีดเส้นใต้
'_') แบบละเลยชื่อมอดูล
>>> from fibo import * >>> fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377
- เลือกอิมพอร์ตบางฟังก์ชัน
6.1.1 การค้นหาพาธของมอดูล (The Module Search Path)
- จะหาที่ไดเรกทอรีปัจจุบันก่อน
- ถ้าไม่พบจะไปหาจากค่าในตัวแปรแวดล้อม
PYTHONPATH - ตามด้วยพาธของไพธอนเอง สำหรับเดเบียนคือ
/usr/lib/python2.4(ตัวเลข 2.4 จะเปลี่ยนไปตามรุ่นไพธอนที่ใช้)
การค้นพาธที่ว่า สามารถดูได้จากตัวแปร sys.path ในไพธอน ซึ่งในทางปฏิบัติเราสามารถแก้ไขได้จากในโปรแกรม ทำให้การเขียนโปรแกรมมีความยืดหยุ่น
6.1.2 ไฟล์ที่ถูกแปลแล้ว (Compiled Python files)
ไพธอนเร่งความเร็วตอนเริ่มโปรแกรมด้วยการแปล (compile) เอาไว้ เช่นถ้าเรามีไฟล์ชื่อ spam.py ถ้าไฟล์นี้ถูกอิมพอร์ต ไพธอนจะคอมไพล์แล้วเก็บในชื่อ spam.pyc ซึ่งไฟล์นี้จะไม่ขึ้นกับระบบปฏิบัติการ หมายความว่าเราสามารถคัดลอกไฟล์นามสกุล .pyc ไปใช้กับเครื่องต่างระบบได้เลย
สำหรับเซียน
- ถ้าเรียกใช้ไพธอนด้วยพารามิเตอร์
-Oไพธอนจะคอมไพล์ไฟล์ให้เล็ก โดยนามสกุลจะกลายเป็น.pyoแทน - ถ้าเรียกใช้ไพธอนด้วยพารามิเตอร์
-OOมีผลเหมือนอันแรกแต่จะลบข้อมูลที่เป็น docstring ออก - โปรแกรมไม่ได้รันเร็วขึ้น เพียงแต่ถูกโหลดได้เร็วขึ้น
- ในการรันปกติ ไพธอนไม่ได้คอมไพล์ไฟล์ที่ถูกรัน แต่จะคอมไพล์เมื่อถูกอิมพอร์ต ดังนั้นถ้าจะเร่งความเร็วตอนถูกโหลด เราอาจแบ่งโปรแกรมหลักของเราให้เล็กลง แล้วไปอิมพอร์ตมอดูลที่เราแบ่งเอาไว้อีกทีนึง
- ตอนรัน ถ้ามีไฟล์
.pycหรือ.pyoอยู่แล้ว ก็ไม่จำเป็นต้องมีไฟล์ต้นฉบับ (ซึ่งนิยมใช้นามสกุลเป็น.py) ดังนั้นหากไม่ต้องการแพร่ซอร์สโค้ด อาจแจกจ่ายเป็นไฟล์คอมไพล์เหล่านี้แทน - ใช้มอดูล compileall ในการคอมไพล์ไฟล์ทั้งไดเรกทอรีได้
6.2 มอดูลมาตรฐาน (Standard Modules)
ไพธอนมีมอดูลมาตรฐานเยอะมาก ดูได้จาก บรรณสารของไพธอน (Python Library Reference) มอดูลหลายตัวเป็นมอดูลของระบบ บางตัวอาจเรียกใช้ได้ในบางสถานะ
ตัวอย่างเช่น มอดูล sys ซึ่งเป็นมอดูลระบบ
- ตัวแปร
sys.ps1จะเก็บข้อความที่เป็นพร็อมต์หลัก (Primary prompt) และsys.ps2จะเก็บพร็อมต์ตาม (Secondary prompt) ตัวแปรทั้งสองจะเรียกใช้ได้ในหมวดโต้ตอบเท่านั้น
>>> import sys >>> sys.ps1 '>>> ' >>> sys.ps2 '... ' >>> sys.ps1 = 'C> ' C> print 'Yuck!' Yuck! C>
- ตัวแปร
sys.pathเก็บพาธการค้นหาของระบบในรูปของลิสต์ ดังนั้นเราอาจเพิ่มพาธการค้น ได้โดยการเพิ่มหรือเปลี่ยนค่า
>>> import sys >>> sys.path.append('/ufs/guido/lib/python')
6.3 ฟังก์ชัน dir() (The dir() Function)
ฟังก์ชัน dir() ใช้ดูว่ามอดูลนั้นประกอบไปด้วยรายชื่อ (คือตัวแปร มอดูล ฟังก์ชัน คลาส หรืออะไรก็ตามที่เราสร้างไว้) อะไรบ้าง เก็บค่าเป็นลิสต์
>>> import fibo, sys >>> dir(fibo) ['__name__', 'fib', 'fib2'] >>> dir(sys) ['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__stdin__', '__stdout__', '_getframe', 'api_version', 'argv', 'builtin_module_names', 'byteorder', 'callstats', 'copyright', 'displayhook', 'exc_clear', 'exc_info', 'exc_type', 'excepthook', 'exec_prefix', 'executable', 'exit', 'getdefaultencoding', 'getdlopenflags', 'getrecursionlimit', 'getrefcount', 'hexversion', 'maxint', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'version', 'version_info', 'warnoptions']
ใช้ dir() เฉย ๆ จะดูรายชื่อในสภาพแวดล้อมของปัจจุบัน
>>> a = [1, 2, 3, 4, 5] >>> import fibo >>> fib = fibo.fib >>> dir() ['__builtins__', '__doc__', '__file__', '__name__', 'a', 'fib', 'fibo', 'sys']
เพื่อไม่ให้รกรุงรัง dir() จึงไม่ยอมดูฟังก์ชันบิลต์อิน (build-in function) ของไพธอนให้ แต่ถ้าเราอยากดู ต้องเรียกผ่านมอดูล __builtin__
>>> import __builtin__ >>> dir(__builtin__) ['ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'IOError', 'ImportError', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', 'abs', 'apply', 'basestring', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min', 'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
6.4 แพกเกจ (Packages)
เวลามีมอดูลที่เราสร้างขึ้นเยอะ เราจะจัดกลุ่มให้ไปรวมในไดเรกทอรีต่างหาก (ทำเหมือนเวลาเรามีไฟล์เยอะ ๆ แล้วเราจะจัดระเบียบไฟล์ ในระบบไฟล์เรียงไดเรกทอรีด้วย / เช่น dira/dirb แต่ไพธอนเรียงด้วย . เช่น pa.pb) ไพธอนเรียกวิธีจัดการกลุ่มมอดูลนี้ว่า แพกเกจ ซึ่งชื่อแพกเกจก็คือชื่อไดเรกทอรีนั่นเอง
สมมุติว่าเราสร้างมอดูลที่ใช้จัดการเสียง (เพลง) ขึ้นมามอดูลหนึ่ง การทำงานมีทั้ง การจัดการรูปแบบไฟล์เสียง มีทั้งการปรุงแต่งเสียง และมีทั้งการกรองเสียง ซึ่งต้องอาศัยการทำงานที่ต่างกัน เราควรเขียนโค้ดแยกแต่ละส่วนออกจากกัน และจัดรวมเป็นแพกเกจ แพกเกจเราจะมีโครงสร้างดังนี้
Sound/ Top-level package
__init__.py Initialize the sound package
Formats/ Subpackage for file format conversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
Effects/ Subpackage for sound effects
__init__.py
echo.py
surround.py
reverse.py
...
Filters/ Subpackage for filters
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
เมื่อแพกเกจเราถูกอิมพอร์ต ไพธอนก็จะค้นพาธจาก sys.path เมื่อพบแล้วก็จัดการคอมไพล์เพื่อจะถูกเรียกใช้ต่อไป
ไฟล์ __init__.py ใช้บอกไพธอนว่า ในไดเรกทอรีนี้เป็นแพกเกจ ซึ่งไฟล์นี้อาจเป็นไฟล์เปล่า ๆ ก็ได้ หรืออาจบรรจุโค้ดที่ใช้เริ่มงานแพกเกจ หรืออาจใส่ค่าตัวแปร __all__ ที่จะใช้บอกว่าแพกเกจนี้จะต้องโหลดมอดูลไหนบ้าง
เวลาเรียกใช้ เราอาจเลือกเรียกเฉพาะมอดูลที่ต้องการก็ได้ ไม่จำเป็นต้องเรียกทั้งหมด
เรียกใช้ได้สองแบบ
- แบบแรกคือ
import ... [ as ... ]
เรียกได้สองลักษณะ- ถ้าไม่มี as
import Sound.Effects.echo
เวลาอ้างถึงต้องอ้างแบบเต็ม ๆ
Sound.Effects.echo.echofilter(input, output, delay=0.7, atten=4)
- ถ้ามี as
import Sound.Effects.echo as SEe
อ้างถึงโดยใช้ชื่อย่อ
SEe.echofilter(input, output, delay=0.7, atten=4)
- ถ้าไม่มี as
- แบบที่สองคือ
from ... import ...
from Sound.Effects import echo
เวลาเรียกใช้ เรียกเฉพาะชื่อ มอดูล.ฟังก์ชัน ที่เราอิมพอร์ตเข้ามา
echo.echofilter(input, output, delay=0.7, atten=4)
หรือถ้าอิมพอร์ตเจาะเฉพาะฟังก์ชัน ก็เรียกเฉพาะฟังก์ชัน
from Sound.Effects.echo import echofilter echofilter(input, output, delay=0.7, atten=4)
หมายเหตุ
- ระหว่างการอิมพอร์ต ถ้าเกิดข้อผิดพลาดขึ้นมา ไพธอนจะแจ้ง
ImportError - อิมพอร์ตได้เฉพาะสิ่งที่มีตัวตนและสิ่งที่ยอมให้อิมพอร์ตได้เท่านั้น คือ แพกเกจหรือมอดูล ที่เหลือนอกจากนี้ คือคลาส ฟังก์ชัน หรือตัวแปร ไม่สามารถอิมพอร์ตได้
6.4.1 เข้าใจการอิมพอร์ต (Importing * From a Package)
เวลาถูกเรียกอิมพอร์ต ไพธอนใช้ตัวแปร __all__ ในไฟล์ __init__.py สำหรับระบุว่าในแพกเกจนี้จะต้องเรียกใช้งานมอดูลไหนบ้าง (แทนการดูจากชื่อไฟล์ในไดเรกทอรีเพราะมีข้อจำกัดมากสำหรับระบบปฏิบัติการที่หลากลาย)
เช่น ในไฟล์ Sounds/Effects/__init__.py อาจมีเนื้อไฟล์เป็น
__all__ = ["echo", "surround", "reverse"]
นั่นคือเมื่อไพธอนพบคำสั่งว่า from Sound.Effects import * เขาจะอิมพอร์ตมอดูลทั้งสามตัวเข้ามา
แต่ถ้าเราไม่ได้กำหนดค่าให้กับ __all__ เวลาไพธอนพบคำสั่ง from Sound.Effects import * เขาจะเพียงแค่รันไฟล์ __init__.py และรับรู้ว่ามีมอดูลอะไรในแพกเกจบ้างเท่านั้น (ไม่ได้คอมไพล์และอิมพอร์ตมอดูลเข้ามาใน namespace เพื่อเตรียมพร้อมจริง ๆ)
หากใช้ในรูปแบบของ from package import module ควรระวังเรื่องชื่อมอดูลหรือฟังก์ชันซ้ำ อาจทำให้เรียกใช้ผิด
6.4.2 การอ้างถึงกันระหว่างมอดูลในแพกเกจ Intra-package References
มีหลักอยู่ว่า
- ถ้าอยู่ในระดับชั้นไดเรกทอรีเดียวกัน เรียกได้โดยตรง โดยไม่ต้องมีชื่อแพกเกจนำหน้า เช่นจากตัวอย่าง มอดูล
surroundสามารถเรียกใช้มอดูลechoได้โดยตรง
import echo echo.echofilter(input, output, delay=0.7, atten=4)
หรือ
from echo import echofilter echofilter(input, output, delay=0.7, atten=4)
- ถ้าอยู่ลึกลงไป สามารถเรียกผ่านจากระดับเดิมได้ทันที
- นอกเหนือจากนี้ ต้องอ้างอิงแบบเต็มยศ เหมือนการเรียกจากมอดูลจากแพกเกจอื่น
- *** เว้นแต่ไพธอนรุ่น 2.5 ขึ้นไป จะสามารถเรียกย้อนขึ้นได้ด้วย ตัวอย่างการเรียกคือ
from . import echo from .. import Formats from ..Filters import equalizer
6.4.3 หนึ่งแพกเกจหลายไดเรกทอรี (Packages in Multiple Directories)
ต้องใส่ชื่อไดเรกทอรีที่บรรจุมอดูลย่อยไว้ในตัวแปรพิเศษชื่อ __path__ ซึ่งเป็นลิสต์ เก็บค่าพาธของมอดูลย่อยไว้













Post new comment