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.itername
จึงไม่ต้องกังวลเรื่องชื่อตัวแปรในโมดูลจะซ้ำกับตัวแปรในโปรแกรมหลัก - โมดูลสามารถอิมพอร์ตโมดูลอื่นได้
- เขียนรูปแบบการอิมพอร์ตได้หลากหลาย
- เลือกอิมพอร์ตบางฟังก์ชั่น
>>> 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__
ซึ่งเป็นลิสต์ เก็บค่าพาธของโมดูลย่อยไว้
- Printer-friendly version
- Log in or register to post comments
- 8205 reads
Recent comments