You are on page 1of 65

Python Tutorial (เรียนรูไพธอน) http://debianclub.

org/book/export/html/166

Python Tutorial (เรียนรูไพธอน)


หมายเหตุจาก webmaster: คุณ wd เขียนบทความชุดนี้ไวไดเกือบแปดเดือนแลวครับ แต webmaster ตรวจไมเสร็จ
สักที ตอนนี้ก็ยังไมเสร็จ แตเห็นวาดองไวนานก็นาเสียดาย จึงเผยแพรทั้งที่ยังตรวจไมเสร็จไปกอน

เอามาจาก Python Tutorial

ตั้งใจเขียนใหอานสนุก ๆ นะครับ อยาจริงจัง

1. เกริ่น (Whetting Your Appetite)


ไพธอนเปน ...

ภาษาสคริปต ทํางานแบบแปลทีละบรรทัดคําสั่ง (interpreter)


มีโหมดโตตอบ (interactive) ทําใหทดลองเลน และทดสอบแบบสั้น ๆ ได
สามารถเขียนใหสั้นกระชับและอานงาย เนื่องจากใชยอหนาในการกําหนดบล็อค
สามารถขยายเชื่อมตอกับภาษา C ได
รันไดคอนขางเร็ว เมื่อเทียบกับภาษาสคริปตตัวอื่น ๆ ซึ่งอาจดูผลทดลองเปรียบเทียบไดที่
shootout.alioth.debian.org: Python benchmarks

สําหรับเดเบียน ไพธอนจะถูกติดตั้งมาเปนคาปริยาย แตหากถูกถอดออกไปแลว ก็สามารถติดตั้งใหมดวยคําสั่ง


$ sudo aptitude install python

2. ใชงานไพธอนแบบบรรทัดคําสั่ง (Using the Python


Interpreter)
2.1 เรียกใชตัวแปลบรรทัดคําสั่ง (Invoking the Interpreter)
2.2 ตัวแปลคําสั่งและสภาพแวดลอม (The Interpreter and Its Environment)

2.1 เรียกใชตัวแปลบรรทัดคําสั่ง (Invoking the Interpreter)

ในเดเบียนตัวโปรแกรมไพธอนจะอยูที่ /usr/bin/python ซึ่งจะเปนลิงกโยงไปหาตัวไพธอนรุนที่เราติดตั้งจริง ๆ เชน


$ which python
/usr/bin/python
$ ls -l /usr/bin/python
lrwxrwxrwx 1 root root 9 2006-12-26 19:18 /usr/bin/python -> python2.4

จะเห็นวารุนของไพธอนตามตัวอยาง เปนรุน 2.4

ซึ่งไดเรกทอรี /usr/bin จะอยูในพาธการคนหาโปรแกรมอยูแลว ดังนั้นเราสามารถเรียกใชไดงาย ๆ วา


$ python

สําหรับลินุกซดิสโทรอื่นก็คลาย ๆ กัน

1 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ในวินโดวส เมื่อติดตั้งไพธอนเสร็จแลว ตัวโปรแกรมจะไปอยูที่ C:\Python24 ซึ่งเราอาจตองเพิ่มในพาธเอง ดวยการ


เขาสู Command Prompt และพิมพดังนี้
set path=%path%;C:\python24

ทั้งหมดนี้เปนการเรียกใชตัวแปลบรรทัดคําสั่งของไพธอน ซึ่งจะทําใหเราสามารถใชงานแบบโตตอบกับตัวแปลภาษา
ได

แตยังมีอีกกรณีหนึ่งคือ หากเราตองการเพียงแคทดลองเพียงคําสั่งเดียวแลวออกจากไพธอนเลย เราอาจใชรูปแบบ


เปน python -c command [arg] เชน
$ python -c 'print "abcd"'
abcd

2.1.1 การสงผานคา (Argument Passing)

เมื่อเรียกใชตัวแปลบรรทัดคําสั่ง คาตามหลังที่เราสงผานใหกับโปรแกรม จะถูกเก็บไวที่ตัวแปร sys.argv โดย...

ถาหากเรียกโดยไมมีคาตามหลัง เชน python : sys.argv[0] จะเปนสตริงวาง


หากเรียกดวย python - : sys.argv[0] จะบรรจุคา "-"
หากเรียกดวยตัวเลือก -c หรือ -m : sys.argv[0] จะเก็บคา "-c" และชื่อมอดูลตามลําดับ คาอื่นหลังจากนี้ จะ
ถูกเก็บไวในตัวแปร sys.argv เพื่อใหคําสั่งหรือมอดูลไดเรียกใชงานตอไป

2.1.2 ใชงานแบบโตตอบ (Interactive Mode)

พร็อมตหลัก (Primary prompt) คือ >>>


$ python
Python 2.4.4 (#2, Apr 5 2007, 20:11:18)
[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

แตหากยังไมจบบล็อค จะใชพร็อมตตาม (Secondary promt) คือ ... ดังนี้


>>> the_world_is_flat = 1
>>> if the_world_is_flat:
... print "Be careful not to fall off!"
...
Be careful not to fall off!

2.2 ตัวแปลคําสั่งและสภาพแวดลอม (The Interpreter and Its Environment)

2.2.1 การจัดการขอผิดพลาด (Error Handling)

เมื่อเกิดขอผิดพลาด ไพธอนจะรายงานขอผิดพลาดและรายทางของการผิดพลาด โดย

ถาอยูในโหมดโตตอบ ไพธอนจะกลับสูพร็อมตหลัก
ถาอยูในโหมดทํางานจากไฟล ไพธอนจะออกมาดวยตัวเลขรายงานสถานะที่ไมใชศูนย (ถาเปนศูนยคือไมเกิด
ขอผิดพลาด)

รายงานความผิดพลาดทั้งหมดจะถูกสงไปยังสายการรายงานขอผิดพลาดของระบบ (standard error stream) และการ


แสดงผลก็จะถูกสงไปยังเอาตพุตของระบบเชนกัน

2 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

การกดคียเพื่อขัดจังหวะ เชน Control-C หรือ DEL จะ...

ถาอยูที่เครื่องหมายพร็อมต จะยกเลิกการปอนขอมูลทั้งหมด และกลับสูพร็อมตหลัก


ถาเปนขณะรัน จะปาวสิ่งผิดปรกติ KeyboardInterrupt ขึ้นมา ซึ่งเราสามารถจัดการไดดวยประโยคคําสั่ง try

2.2.2 การทําใหสคริปตรันได (Executable Python Scripts)

แคเติม Hash bang ที่บรรทัดแรกของสคริปต ก็จะทําใหสคริปตสามารถรันได


#! /usr/bin/env python

อยาลืมเปลี่ยนโหมดใหรันไดดวย
$ chmod +x myscript.py

2.2.3 การแจงรหัสอักขระของซอรสโคด (Source Code Encoding)

หากมีสวนของซอรสโคดที่ไมใชภาษาอังกฤษลวน ควรแจงรหัสอักขระที่ใชดวย ดวยการเติมตอจากบรรทัดแรกวา


# -*- coding: encoding -*-

สามารถดูรหัสอักขระทั้งหมดที่ Python Library Reference ในหัวขอ codecs

เชน ถาจะใชสตริงที่มีสัญลักษณหนวยเงินยูโร เราอาจเลือกใชรหัสอักขระ ISO-8859-15 (คา ordinal คือ 164) ซึ่งใน


สคริปตนี้จะพิมพคา 8364 ซึ่งเปนคายูนิโคดของสัญลักษณยูโร
# -*- coding: iso-8859-15 -*-

currency = u"€"
print ord(currency)

แตสําหรับยุคนี้ ควรเลือกใช UTF-8 ดีกวาเยอะ เพราะรองรับอักขระทุกตัวในโลก

2.2.4 การสรางไฟลเริ่มตนสําหรับโหมดโตตอบ (The Interactive Startup File)

ในการใชงานโหมดโตตอบ บางครั้งอาจตองตั้งคาเริ่มตนใหระบบ ซึ่งทําไดโดยใสชื่อโปรแกรมที่ตองการรันในขณะ


เริ่มตนในตัวแปรแวดลอมชื่อ PYTHONSTARTUP (คลายกับการตั้งคาไฟล .profile ในลินุกซ)

เราสามารถขยายความสามารถนี้ โดยใหไฟลเริ่มตนหลักมาดูในไดเรกทอรีปจจุบันกอน วามีไฟลที่มีชื่อตามที่เรา


กําหนดหรือไม ถามีก็จะรันตามที่เรากําหนดไว เชนถากําหนดใหใชชื่อวา .pythonrc.py ก็ใชคําสั่งวา "if
os.path.isfile('.pythonrc.py'): execfile('.pythonrc.py')" เปนตน

โปรแกรมเริ่มตนนี้จะถูกเรียกในโหมดโตตอบเทานั้น จะไมถูกเรียกในการรันสคริปต แตถาตองการใหสคริปตเรียก


โปรแกรมเริ่มตนนี้ดวย จะตองสั่งในสคริปตเอง เชน
import os
filename = os.environ.get('PYTHONSTARTUP')
if filename and os.path.isfile(filename):
execfile(filename)

3. รูจักไพธอน (An Informal Introduction to Python)


3.1 ใชงานเปนเครื่องคิดเลข (Using Python as a Calculator)
3.2 ลองเขียนสักสคริปตนึง (First Steps Towards Programming)

3 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ตัวอยางจะใชเครื่องหมาย # แทนคอมเมนต เชน


# this is the first comment
SPAM = 1 # and this is the second comment
# ... and now a third!
STRING = "# This is not a comment."

3.1 ใชงานเปนเครื่องคิดเลข (Using Python as a Calculator)

3.1.1 ตัวเลข (Numbers)

ในโหมดโตตอบ เราใชแทนเครื่องคิดเลขไดเลย เชน


>>> 2+2
4

>>> # This is a comment


... 2+2
4

>>> 2+2 # and a comment on the same line as code


4

>>> (50-5*6)/4
5

>>> # Integer division returns the floor:


... 7/3
2

>>> 7/-3
-3

กําหนดคาใหตัวแปรดวย = ตามปกติ
>>> width = 20
>>> height = 5*9
>>> width * height
900

กําหนดคาทีละหลายตัวแปรพรอมกันก็ได
>>> x = y = z = 0 # Zero x, y and z
>>> x
0

>>> y
0

>>> z
0

รองรับทศนิยมจุดลอยตัว (floating point) ดวย โดยมีหลักวา ถาตนทางเปนจํานวนเต็ม ผลจะเปนจํานวนเต็ม ถาตน


ทางเปนทศนิยม ผลจะเปนทศนิยม (ถาผสมกันก็จะเปนทศนิยมเชนกัน)
>>> 3 * 3.75 / 1.5
7.5

>>> 7 / 2
3

4 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> 7.0 / 2
3.5

จํานวนเชิงซอนก็ได โดยตัวเลขจินตภาพจะตองตอทายดวย "j" หรือ "J" โดยมีรูปแบบเปน "(real+imagj)" หรือ


ใชฟงกชัน "complex(real, imag)" ก็ได
>>> 1j * 1J
(-1+0j)

>>> 1j * complex(0,1)
(-1+0j)

>>> 3+1j*3
(3+3j)

>>> (3+1j)*3
(9+3j)

>>> (1+2j)/(1+1j)
(1.5+0.5j)

ตัวเลขในจํานวนเชิงซอน จะเก็บในรูปทศนิยมเสมอ และสามารถแยกสวนจริงกับสวนจินตภาพได ดวยการเขียนในรู


ปออบเจกตคือ z.real และ z.imag
>>> a=1+0.5j
>>> a.real
1.0

>>> a.imag
0.5

ไมสามารถใชฟงกชันการแปลงตัวเลขปกติ คือ float(), int() และ long() กับจํานวนเชิงซอนได แตสามารถใช


ฟงกชัน abs(z) หาคาสัมบูรณ และใช z.real หาคาสวนจริงของ z ได
>>> a=3.0+4.0j

>>> float(a)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: can't convert complex to float; use abs(z)

>>> a.real
3.0

>>> a.imag
4.0

>>> abs(a) # sqrt(a.real**2 + a.imag**2)


5.0

พิเศษสําหรับโหมดโตตอบ คาที่ถูกพิมพออกมาเปนครั้งสุดทาย จะถูกเก็บไวในตัวแปรพิเศษคือ _ เราอาจนําตัวแปรนี้


ไปใชในการคํานวนคาตอ ๆ ไปได
>>> tax = 12.5 / 100
>>> price = 100.50
>>> price * tax
12.5625

>>> price + _
113.0625

>>> round(_, 2)
113.06

5 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

*** เนื่องจาก _ เปนตัวแปรพิเศษดังกลาว ดังนั้นเพื่อปองกันความผิดพลาด จึงไมควรกําหนดคาใหมัน ควรใชเปนตัว


แปรเฉพาะตามวัตถุประสงคของไพธอนเทานั้น

3.1.2 สตริง (Strings)

สามารถใชงานสตริงไดทั้งอัญประกาศเดี่ยวและคู (Single & Double quote)


>>> 'spam eggs'
'spam eggs'

>>> 'doesn\'t'
"doesn't"

>>> "doesn't"
"doesn't"

>>> '"Yes," he said.'


'"Yes," he said.'

>>> "\"Yes,\" he said."


'"Yes," he said.'

>>> '"Isn\'t," she said.'


'"Isn\'t," she said.'

ใชสตริงแบบหลายบรรทัด โดย...

ใชอักขระ \ (Backslash) ในการแยก เชน


>>> hello = "This is a rather long string containing\n\
... several lines of text just as you would do in C.\n\
... Note that whitespace at the beginning of the line is\
... significant."

>>> print hello


This is a rather long string containing
several lines of text just as you would do in C.
Note that whitespace at the beginning of the line is significant.

ใชสตริงดิบ (raw string) โดยใช r นําหนาเครื่องหมายอัญประกาศ ซึ่งก็จะใหผลแบบดิบ ๆ


>>> hello = r"This is a rather long string containing\n\
... several lines of text much as you would do in C."

>>> print hello


This is a rather long string containing\n\
several lines of text much as you would do in C.

หรือใชอัญประกาศสามตัว คือ """ หรือ ''' อันนี้ใชงายแบบธรรมชาติ


>>> print """
... Usage: thingy [OPTIONS]
... -h Display this usage message
... -H hostname Hostname to connect to
... """
Usage: thingy [OPTIONS]
-h Display this usage message
-H hostname Hostname to connect to

ที่สําคัญคือ เริ่มตนดวยอัญประกาศแบบไหน ก็ตองปดทายดวยอัญประกาศแบบนั้นเสมอ

ใช + ในการเชื่อมสตริง และ * ในการซ้ําขอความตามจํานวนที่กําหนด

6 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> word = 'Help' + 'A'


>>> word
'HelpA'

>>> '<' + word*5 + '>'


'<HelpAHelpAHelpAHelpAHelpA>'

ชองวางระหวางสตริงจะถูกตีความเปนการเชื่อมสตริง แตใชไดกับคาคงที่ (literal) เทานั้น ตัวแปรหรือฟงกชันไมเกี่ยว


>>> 'str' 'ing' # <- This is ok
'string'

>>> 'str'.strip() + 'ing' # <- This is ok


'string'

>>> 'str'.strip() 'ing' # <- This is invalid


File "<stdin>", line 1, in ?
'str'.strip() 'ing'
^
SyntaxError: invalid syntax

สตริงทําตัวเปน แอรเรยของอักขระ ดังนั้นจึงสามารถอางถึงแบบแอรเรยได โดยสามารถอางเปนชวงดวย : (colon)


ได ซึ่งไพธอนเรียกชวงของสตริงนี้วาสไลซ (slice)
>>> word[4]
'A'

>>> word[0:2]
'He'

>>> word[2:4]
'lp'

ถาละเลย ไมใสคาดัชนี ถาเปนดัชนีขางหนา จะถูกตีความเปนศูนย และตัวหลังจะถูกตีความเปนความยาวสตริง


>>> word[:2] # The first two characters
'He'

>>> word[2:] # Everything except the first two characters


'lpA'

สตริงในไพธอนไมเหมือนกับภาษาซี ไพธอนไมสามารถเปลี่ยนคาสตริงโดยอางจากดัชนีได
>>> word[0] = 'x'
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object doesn't support item assignment

>>> word[:1] = 'Splat'


Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: object doesn't support slice assignment

แตก็สรางใหมไดไมยาก
>>> 'x' + word[1:]
'xelpA'

>>> 'Splat' + word[4]


'SplatA'

>>> x = word[:]
>>> x = 'Splat' + x[4]
>>> x
'SplatA'

7 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ลูกเลนเล็กนอย ใหผลเปน s เหมือนเดิม


>>> word[:2] + word[2:]
'HelpA'

>>> word[:3] + word[3:]


'HelpA'

ดัชนีที่เปนคานอกชวงที่มีจริง ไพธอนจะแสดงเปนอักขระวางให โดยไมรายงานความผิดพลาด


>>> word[1:100]
'elpA'

>>> word[10:]
''

>>> word[2:1]
''

ดัชนีที่เปนคาลบ จะเปนการนับจากขวามาซาย
>>> word[-1] # The last character
'A'

>>> word[-2] # The last-but-one character


'p'

>>> word[-2:] # The last two characters


'pA'

>>> word[:-2] # Everything except the last two characters


'Hel'

ยกเวน -0 มีคาเทากับ 0 จึงนับจากซายเปนปกติ


>>> word[-0] # (since -0 equals 0)
'H'

ถาระบุดัชนีเปนชวง ไพธอนจะจัดการคาที่ไมเปนจริงใหทั้งหมด แตถาใชดัชนีตัวเดียว ถาคาดัชนีไมเปนจริง ไพธอน


จะแสดงขอผิดพลาด
>>> word[-100:]
'HelpA'

>>> word[-10] # error


Traceback (most recent call last):
File "<stdin>", line 1, in ?
IndexError: string index out of range

เทคนิคการจําเรื่องชวงดัชนีคือ ใหคิดวาดัชนีเปนคาที่อยูระหวางอักขระ ที่ซายสุดเปน 0 และขวาสุดเปนความยาวสต


ริง
+---+---+---+---+---+
| H | e | l | p | A |
+---+---+---+---+---+
0 1 2 3 4 5
-5 -4 -3 -2 -1

สําหรับดัชนีที่เปนคาบวก ขนาดของสไลซคือผลตางของคาตัวเลขดัชนี เชนขนาดของ word[1:3] คือ 2

เราหาความยาวของสตริงไดดวยฟงกชัน len()
>>> s = 'supercalifragilisticexpialidocious'

8 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> len(s)
34

ดูเพิ่มเติม
เรื่อง

ชนิดขอมูลแบบลําดับ (Sequence Types)


เมธอดของสตริง (String Methods)
การจัดรูปแบบสตริง String Formatting Operations

3.1.3 สตริงแบบยูนิโคด (Unicode Strings)

เริ่มใชในไพธอนรุน 2.0 ในการจัดการขอมูลแบบยูนิโคด (ดู http://www.unicode.org/)

ใชงานเหมือนสตริงปกติ เวลาเขียนใช u นําหนาอัญประกาศ


>>> u'Hello World !'
u'Hello World !'

ถาอักขระยูนิโคดตัวไหนพิมพยาก อาจใช \u (Python Unicode-Escape) นําหนา เชน ตัวอยางการใชอักขระเวนวรรค


(space) คือ \u0020
>>> u'Hello\u0020World !'
u'Hello World !'

ในการใชสตริงดิบกับยูนิโคด ใช ur นําหนา และตองใสอักขระ \ สองตัว ไมงั้นจะตีความเปน Python Unicode-


Escape ดูยุงยากเล็กนอย แตจะมีประโยชนสําหรับการใชงาน regular expresstion
>>> ur'Hello\u0020World !'
u'Hello World !'

>>> ur'Hello\\u0020World !'


u'Hello\\\\u0020World !'

จริง ๆ แลว ไพธอนเก็บขอมูลสตริงเปนอักขระยูนิโคดอยูแลว (อาจเปลี่ยนแปลงในรุนหนา คือรุน 3.0) ดังนั้นการใช


ฟงกชัน str() กับอักขระยูนิโคด จึงแสดงขอผิดพลาด เวนเสียแตวาอักขระเหลานั้นมีรหัส ASCII นอยกวา 127 คือ
เปนภาษาอังกฤษธรรมดา
>>> u"abc"
u'abc'

>>> str(u"abc")
'abc'

>>> u"äöü"
u'\xe4\xf6\xfc'

>>> str(u"äöü")
Traceback (most recent call last):
File "<stdin>", line 1, in ?
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2:
ordinal not in range(128)

การแปลงรหัสอักขระไปเปนรหัสอักขระอื่น ๆ เราใชฟงกชัน encode()


>>> u"äöü".encode('utf-8')
'\xc3\xa4\xc3\xb6\xc3\xbc'

เปนการแปลงสตริง u"äöü" ไปเปนสตริงแบบ utf-8

9 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

และใชฟงกชัน unicode() ในการแปลงกลับ


>>> unicode('\xc3\xa4\xc3\xb6\xc3\xbc', 'utf-8')
u'\xe4\xf6\xfc'

3.1.4 ลิสต (Lists)

ขอมูลลิสตถือเปนขอเดนที่สุดขอหนึ่งของไพธอน เปนการเอาขอมูลมาจัดใหอยูในกลุมเดียวกัน ภายใตเครื่อง


หมายวงเล็บ [] โดยขอมูลยอยไมจําเปนตองเปนชนิดเดียวกัน
>>> a = ['spam', 'eggs', 100, 1234]
>>> a
['spam', 'eggs', 100, 1234]

การใชดัชนีในการอางถึงขอมูลยอยภายใน ใชงานคลายกับสตริง
>>> a[0]
'spam'

>>> a[3]
1234

>>> a[-2]
100

>>> a[1:-1]
['eggs', 100]

>>> a[:2] + ['bacon', 2*2]


['spam', 'eggs', 'bacon', 4]

>>> 3*a[:3] + ['Boo!']


['spam', 'eggs', 100, 'spam', 'eggs', 100, 'spam', 'eggs', 100, 'Boo!']

แตตางจากสตริงตรงที่ขอมูลยอยภายในสามารถเปลี่ยนแปลงคาได โดยการอางจากดัชนี
>>> a
['spam', 'eggs', 100, 1234]

>>> a[2] = a[2] + 23


>>> a
['spam', 'eggs', 123, 1234]

>>> a[2] = 10
>>> a
['spam', 'eggs', 10, 1234]

กําหนดคาเปนชวงสไลซก็ได
>>> # Replace some items:
... a[0:2] = [1, 12]
>>> a
[1, 12, 123, 1234]

>>> # Remove some:


... a[0:2] = []
>>> a
[123, 1234]

>>> # Insert some:


... a[1:1] = ['bletch', 'xyzzy']
>>> a
[123, 'bletch', 'xyzzy', 1234]

10 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> # Insert (a copy of) itself at the beginning


... a[:0] = a
>>> a
[123, 'bletch', 'xyzzy', 1234, 123, 'bletch', 'xyzzy', 1234]

>>> # Clear the list: replace all items with an empty list
... a[:] = []
>>> a
[]

ใชฟงกชัน len() ในการหาขนาดลิสต (จํานวนสมาชิก)


>>> len(a)
8

ซอนลิสตในลิสตก็ได
>>> q = [2, 3]
>>> p = [1, q, 4]
>>> len(p)
3

>>> p[1]
[2, 3]

>>> p[1][0]
2

>>> p[1].append('xtra') # See section 5.1


>>> p
[1, [2, 3, 'xtra'], 4]

>>> q
[2, 3, 'xtra']

จากตัวอยางนี้ ตัวแปร p[1] กับตัวแปร q เปนออบเจกตอันเดียวกัน (การใชลิสตตองระวังตรงนี้นิดนึง)

3.2 ลองเขียนสักสคริปตนึง (First Steps Towards Programming)

การใชงานไพธอนงายตรงนี้ คือเราจะเขียนโคดเล็ก ๆ แลวก็จับมาตอ ๆ กันไป กลายเปนสคริปตที่ซับซอนสําหรับใช


งานจริง

เริ่มดวยโคดอมตะ อนุกรมฟโบนัชชี (Fibonacci)


>>> # Fibonacci series:
... # the sum of two elements defines the next
... a, b = 0, 1

>>> while b < 10:


... print b
... a, b = b, a+b
...
1
1
2
3
5
8

ความรูใหม

11 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

บรรทัดแรกเปนการกําหนดหลายตัวแปรทีละหลายคา (multiple assignment) ในที่นี้ a คือ 0 และ b คือ 1


การวนรอบโดยใชคําสั่ง while คําสั่งนี้จะวนรอบตราบเทาที่เงื่อนไขลูปเปนจริง นิพจนเงื่อนไขที่เปนจริง คือมี
คาที่ไมใชศูนยหรือคาวาง ซึ่งสามารถเปนสตริง ลิสต ลําดับ หรือนิพจนคณิตศาสตรก็ได โดยในที่นี้ใชนิพจน
เปรียบเทียบ ซึ่งจะใชสัญญลักษณเหมือนภาษาซี คือ < คือนอยกวา > คือมากกวา == คือเทากันกับ <= คือ
นอยกวาหรือเทากับ >= คือมากกวาหรือเทากับ และ != คือไมเทากับ
สังเกตการเยื้องของบล็อค ซึ่งไพธอนใชการเยื้องในการจัดกลุมบล็อค (สําหรับกลุมเดียวกัน ตองเยื้องใหเทา
กัน)
คําสั่ง print มีขอพิเศษคือ
ไมเกี่ยงชนิดขอมูล ทําใหเขียนโคดงาย
>>> i = 256*256
>>> print 'The value of i is', i
The value of i is 65536

ใช , หากไมตองการขึ้นบรรทัดใหม
>>> a, b = 0, 1
>>> while b < 1000:
... print b,
... a, b = b, a+b
...
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

4. คําสั่งควบคุม (More Control Flow Tools)


4.1 ประโยค if (if Statements)
4.2 ประโยค for (for Statements)
4.3 ฟงกชัน range() (The range() Function)
4.4 คําสั่ง break และ continue และวลี else สําหรับการวนรอบ (break and continue Statements, and else
Clauses on Loops)
4.5 คําสั่ง pass (pass Statements)
4.6 นิยามฟงกชัน (Defining Functions)
4.7 เพิ่มเติมเรื่องฟงกชัน (More on Defining Functions)

เมื่อกี้ไดรูจักคําสั่ง while แลว บทนี้เรามารูจักคําสั่งควบคุมใหมากขึ้น

4.1 ประโยค if (if Statements)

>>> x = int(raw_input("Please enter an integer: "))


>>> if x < 0:
... x = 0
... print 'Negative changed to zero'
... elif x == 0:
... print 'Zero'
... elif x == 1:
... print 'Single'
... else:
... print 'More'
...

มี elif กี่ตัวก็ได และมี else หรือไมมีก็ได (ภาษาอื่นอาจมี switch และ case แตไพธอนใช if อยางเดียว)

12 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

4.2 ประโยค for (for Statements)

for ของไพธอน ตางจากภาษาอื่นเล็กนอย ตอนวนรอบ แทนที่จะใชตัวนับซึ่งเปนตัวเลข ไพธอนกลับใชลําดับแทน


(เชน สตริง ลิสต หรือทูเปล)
>>> # Measure some strings:
... a = ['cat', 'window', 'defenestrate']
>>> for x in a:
... print x, len(x)
...
cat 3
window 6
defenestrate 12

4.3 ฟงกชัน range() (The range() Function)

ใชสรางลิสตจากชวงของตัวเลขจํานวนเต็ม

แบบงาย
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

อาจกําหนดเปนชวง
>>> range(5, 10)
[5, 6, 7, 8, 9]

หรือแบบกําหนดขนาดขั้นของการเพิ่มดวย
>>> range(0, 10, 3)
[0, 3, 6, 9]

>>> range(-10, -100, -30)


[-10, -40, -70]

ใชรวมกับ len() กับลิสต (การทํางานกับลิสตแบบอางอิงจากดัชนี จะใชวิธีนี้เปนปกติ)


>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
... print i, a[i]
...
0 Mary
1 had
2 a
3 little
4 lamb

4.4 คําสั่ง break และ continue และวลี else สําหรับการวนรอบ (break and continue Statements, and else
Clauses on Loops)

คําสั่ง break สงผลใหหลุดจากวงรอบที่คําสั่งนี้บรรจุอยู

สวน continue จะมีผลใหหยุดการทํางานที่จุดนั้น แลวกลับไปเริ่มวนรอบใหม

13 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

วลี else ใชสําหรับเมื่อหลุดจากการวนแลว จะทําภายในบล็อคนี้หนึ่งครั้ง ยกเวนถาพบคําสั่ง break


>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print n, 'equals', x, '*', n/x
... break
... else:
... # loop fell through without finding a factor
... print n, 'is a prime number'
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

4.5 คําสั่ง pass (pass Statements)

คําสั่ง pass ไมทําอะไรเลย แตมีไวเผื่อเวลาเราวางโครงสรางโคดไวแลว แตยังไมไดเขียนทอนนั้น ก็บรรจุคําสั่งนี้ไว


เพื่อใหสามารถทดสอบการรันได
>>> while True:
... pass # Busy-wait for keyboard interrupt
...

4.6 นิยามฟงกชัน (Defining Functions)

เอาตัวอยางในการเขียนอนุกรมฟโบนัชชีมาเขียน
>>> def fib(n): # write Fibonacci series up to n
... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while b < n:
... print b,
... a, b = b, a+b
...

>>> # Now call the function we just defined:


... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

ฟงกชันขึ้นตนดวย def ตามดวยชื่อฟงกชันและวงเล็บซึ่งบรรจุอารกิวเมนต หลังจากนี้จะเปนบล็อคที่ตองเยื้อง


ยอหนา

บรรทัดพิเศษตอจากชื่อฟงกชัน อาจใสคําอธิบายการทํางานของฟงกชันไดเลย ซึ่งไพธอนจะไมตีความเปนโคดที่จะ


รัน บรรทัดนี้เรียกวา docstring

ตัวแปรในฟงกชันจะถือเปนตัวแปรทองถิ่นทั้งหมด เวนแตเรากําหนดใหเปนตัวแปรสวนรวม ซึ่งตองกําหนดดวยคําสั่ง


global

การสงผานคาตัวแปร จะเปนการสงผานโดยคาทั้งหมด (pass by value)

14 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ชื่อฟงกชันสามารถถูกกําหนดคาใหกับตัวแปรได
>>> fib
<function fib at 10042ed0>

>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

>>> f
<function fib at 10042ed0>

ฟงกชันในไพธอนจะคืนคากลับมาเสมอ ซึ่งปกติจะใชคําสั่ง return VALUE แตในตัวอยางขางตนไมมีการคืนคาดวย


คําสั่ง return กรณีนี้ไพธอนจะคืนคาเปนคาพิเศษคือ None
>>> print fib(0)
None

จากตัวอยางขางตน สามารถเขียนในรูปฟงกชันที่สงคืนคาดังนี้
>>> def fib2(n): # return Fibonacci series up to
... """Return a list containing the Fibonacci series up to n.""
... result = []
... a, b = 0, 1
... while b < n:
... result.append(b) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

ความรูใหม

ฟงกชันที่ไมระบุการคืนคา จะสงคากลับเปน None


คําสัง result.append(b) เปนเมธอดของลิสต result เมธอดก็คือฟงกชันที่เปนเฉพาะของออบเจกตนั้น ซึ่ง
มีรูปแบบการเขียนเปน obj.methodname จากตัวอยางการใชเมธอด result.append(b) มีผลเทากับ "result
= result + [b]" แตเขียนไดกระชับและเขาใจงายกวา

4.7 เพิ่มเติมเรื่องฟงกชัน (More on Defining Functions)

มีหลักในการกําหนดคาอารกิวเมนตคือ

4.7.1 แบบกําหนดคาปริยาย (Default Argument Values)

เปนการกําหนดคาปริยายใหกับอารกิวเมนต มีรูปแบบวาอารกิวเมนตที่จะกําหนดคาปริยายให จะตองอยูทางขวาเสมอ


สวนตัวที่ไมกําหนด จะตองอยูทางซายเสมอ
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = raw_input(prompt)
if ok in ('y', 'ye', 'yes'): return True
if ok in ('n', 'no', 'nop', 'nope'): return False
retries = retries - 1
if retries < 0: raise IOError, 'refusenik user'
print complaint

15 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

การใชงานเชน ask_ok('Do you really want to quit?') หรือ ask_ok('OK to overwrite the file?', 2,
'Please answer y or n')

จากตัวอยางหลัง retries คือ 2 และ complaint คือ 'Please answer y or n'

ในตัวอยางนี้ มีคําใหมคือ in เปนการดูวาตัวแปร ok อยูภายในรายการที่กําหนดหรือไม

ขอควรระวัง

การกําหนดคาใหกับอารกิวเมนต จะกําหนดในครั้งแรกครั้งเดียว จึงตองระมัดระวังในการใชงาน ตามตัวอยาง


คือ
>>> i = 5
>>> def f(arg=i):
... print arg
...
>>> f()
5

>>> i=6
>>> f()
5

ตัวแปรที่เปน mutable คือลิสต ทูเปล และอินสแตนซของคลาส ตองระวังในการใชงานอยางยิ่ง เนื่องจากเมื่อ


มันถูกกําหนดคาแลว คาของมันจะยังคงอยูภายในฟงกชันนั้น เวลาอางถึงในรอบหลัง ๆ จะทําใหผิดพลาดได
>>> def f(a, L=[]):
... L.append(a)
... return L
...
>>> print f(1)
[1]

>>> print f(2)


[1, 2]

>>> print f(3)


[1, 2, 3]

วิธีแกคือ ใหหลีกเลื่ยงขอมูลชนิดนี้ในการกําหนดคาปริยาย จากตัวอยางจะดัดแปลงฟงกชันเปน


def f(a, L=None):
if L is None:
L = []
L.append(a)
return L

4.7.2 แบบระบุคียเวิรด (Keyword Arguments)

เหมือนกับหัวขอกอนหนา แตในหัวขอนี้ เจาะจงอธิบายลักษณะที่กําหนดเปนคียเวิรด มีรูปแบบคือ "keyword =


value"

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):


print "-- This parrot wouldn't", action,
print "if you put", voltage, "volts through it."
print "-- Lovely plumage, the", type
print "-- It's", state, "!"

การใชงาน

แบบนี้ใชได

16 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')

แบบนี้ผิด
parrot() # ผิดเพราะขาดคาที่ไมมีคาปริยาย คือ voltage
parrot(voltage=5.0, 'dead') # ผิดเพราะคาปริยายอยูซาย จริง ๆ ตองอยูขวา
parrot(110, voltage=220) # ผิดเพราะกําหนดคาซอน
parrot(actor='John Cleese') # ผิดเพราะชื่อไมมีชื่อคียเวิรด actor

ตัวอยางการรายงานขอผิดพลาดของการกําหนดคาซอน
>>> def function(a):
... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'

ตัวอยางตอไปจะแสดงใหเห็นประโยชนที่แทจริงของหัวขอนี้ คือ การระบุอารกิวเมนตแบบใหงายตอการพลิกแพลง


คือเราสามารถระบุอารกิวเมนตแบบอางอิงได โดยมีรูปแบบคือ
def function_name(normal_parameter, *tuple_parameter, **dictionary_parameter)

ตัวอยางคือ
def cheeseshop(kind, *arguments, **keywords):
print "-- Do you have any", kind, '?'
print "-- I'm sorry, we're all out of", kind
for arg in arguments: print arg
print '-'*40
keys = keywords.keys()
keys.sort()
for kw in keys: print kw, ':', keywords[kw]

เรียกใชดวยคําสั่ง
cheeseshop('Limburger', "It's very runny, sir.",
"It's really very, VERY runny, sir.",
client='John Cleese',
shopkeeper='Michael Palin',
sketch='Cheese Shop Sketch')

ในที่นี้

อารกิวเมนตธรรมดา คือ kind = 'Limburger'


อารกิวเมนตที่เปนทูเปล คือ arguments = ("It's very runny, sir.", "It's really very, VERY
runny, sir.")
อารกิวเมนตที่เปนดิกชันนารี คือ keywords = { client:'John Cleese', shopkeeper:'Michael
Palin', sketch:'Cheese Shop Sketch' }

ผลลัพธคือ
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin

17 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

sketch : Cheese Shop Sketch

ความรูใหม

จากตัวอยาง มีการเรียกใชเมธอด sort() ในการเรียงขอมูลดัชนี ซึ่งเปนเมธอดของลิสต

ลิสตนี้ไดมาจากการหาดัชนีของดิกชันนารี keyword ไดออกมาเปนลิสตชื่อ keys ดวยเมธอดของดิกชันนารีคือ


keys()

4.7.3 การกําหนดจํานวนอารกิวเมนตที่ยืดหยุน (Arbitrary Argument Lists)

หากเราสงผานอารกิวเมนตแบบอางอิงซึ่งจะกลายเปนทูเปลแลว เราจะไดความยืดหยุนในการกําหนดอารกิวเมนต
ตัวอยางคือ
>>> def testparm(x, *y):
... print 'x=',x,'y=',y
...
>>> testparm('a',1,2,3)
x= a y= (1, 2, 3)

หรือ
>>> def testparm(x, *y):
... print 'x=', x, 'y=',
... for i in y:
... print i,
...

>>> testparm('a',1,2,3)
x= a y= 1 2 3

ตัวอยางในบทความตนฉบับคือ
def fprintf(file, format, *args):
file.write(format % args)

4.7.4 ถอดอารกิวเมนตจากลิสตหรือดิกชันนารี (Unpacking Argument Lists)

จากตัวอยางกอน ๆ ที่เรารูเรื่องการผานคาแบบอางอิงเปนทูเปลและดิกชันนารีแลว เราสามารถพลิกแพลงได เชน ใน


ตัวอยางนี้ใชลิสตแทนทูเปล (ถาไมมีการเปลี่ยนแปลงคา ลิสตและทูเปลสามารถใชแทนกันไดแบบตรง ๆ)
>>> range(3, 6) # normal call with separate arguments
[3, 4, 5]

>>> args = [3, 6]


>>> range(*args) # call with arguments unpacked from a list
[3, 4, 5]

ตัวอยางนี้เปนดิกชันนารี
>>> def parrot(voltage, state='a stiff', action='voom'):
... print "-- This parrot wouldn't", action,
... print "if you put", voltage, "volts through it.",
... print "E's", state, "!"
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

4.7.5 แลมบดา (Lambda Forms)

18 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ยืมความสามารถเรื่อง Functional Programming แบบภาษา Lisp มาใช

โครงสรางชวนเวียนหัวหนอย ดูตัวอยางดีกวา

แบบไมใช lambda
>>> def f(x):
... return x*2
...
>>> f(3)
6

ใช lambda แบบแรก


>>> g = lambda x: x*2
>>> g(3)
6

ใช lambda แบบชั่วคราวจริง ๆ


>>> (lambda x: x*2)(3)
6

อีกตัวอยางนึง ใชผสมกับฟงกชัน
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42

>>> f(1)
43

4.7.6 ขอความอธิบายการทํางาน (Documentation Strings)

บรรทัดแรกถัดจากชื่อฟงกชัน เปนบรรทัดพิเศษที่ใสบรรทัดขอความอธิบายการทํางานของฟงกชัน ถาใชอัญประกาศ


สามตัว """ หรือ ''' ก็สามารถเขียนไดหลายบรรทัด

สามารถเรียกดูขอความในบรรทัดนี้ไดจากเมธอด function_name.__doc__

ตัวอยาง
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...

>>> print my_function.__doc__


Do nothing, but document it.

No, really, it doesn't do anything.

5. โครงสรางขอมูล (Data Structure)

19 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

5.1 ลิสตอีกที (More on Lists)


5.2 ประโยค del (The del statement)
5.3 ทูเปล (Tuples and Sequences)
5.4 เซ็ต (Sets)
5.5 ดิกชันนารี (Dictionaries)
5.6 เทคนิคการวนรอบ (Looping Techniques)
5.7 เงื่อนไข (More on Conditions)
5.8 น้ําหนักของขอมูลแบบลําดับ (Comparing Sequences and Other Types)

บทนี้จะอธิบายเทคนิคการใชงานขอมูล

5.1 ลิสตอีกที (More on Lists)

เวลาใชงานจริง เราจะใชลิสตมากหนอย เพราะมันเปลี่ยนแปลงคาได เลยทําใหมีเมธอดของลิสตเยอะหนอย

append(x)
เติมสมาชิกตอทายลิสต มีคาเทียบเทา a[len(a):] = [x]
>>> a=[1,2,3]
>>> a.append(4)
>>> a
[1, 2, 3, 4]

extend(L)
ยกลิสต L ทั้งยวง ไปขยายตอทาย a คือ a[len(a):] = L
>>> a=[1,2,3]
>>> L=[4,5,6]
>>> a.extend(L)
>>> a
[1, 2, 3, 4, 5, 6]

insert(i, x)
แทรกสมาชิก x ในตําแหนง i
a.insert(0, x) ก็คือการไปแทรกขางหนา
a.insert(len(a), x) ก็คือการไปตอทาย คือ a.append(x)

>>> a=[1,2,3]
>>> a.insert(2,0)
>>> a
[1, 2, 0, 3]

remove(x)
ลบสมาชิกตัวแรกที่มีคาเทากับ x ถาไมมีเทาจะเกิดขอผิดพลาด
>>> a=[1,2,3]
>>> a.remove(2)
>>> a
[1, 3]

>>> a.remove(2)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: list.remove(x): x not in list

pop([i])
ถอดสมาชิกในตําแหนง i ออก ถาละเลยไมใสคา i จะถอดตัวสุดทายออกแทน

20 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> a=[1,2,3]
>>> a.pop(1)
2

>>> a
[1, 3]

index(x)
คืนคาดัชนีของลิสต ตัวที่มีคาเทากับ x ถาไมมีคาเทาเลยจะแจงความผิดพลาด
>>> a=[1,2,3]
>>> a.index(2)
1

count(x)
นับจํานวนสมาชิกที่มีคาเทากับ x
>>> a=[2,2,2,9,4,4,4,4]
>>> a.count(2)
3

sort()
จัดเรียงสมาชิก
>>> a=[1,5,4,2,3]
>>> a.sort()
>>> a
[1, 2, 3, 4, 5]

reverse()
กลับตําแหนงสมาชิก
>>> a=[1,5,4,2,3]
>>> a.reverse()
>>> a
[3, 2, 4, 5, 1]

ตัวอยางรวมอีกทีนึง
>>> a = [66.25, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.25), a.count('x')
2 1 0

>>> a.insert(2, -1)


>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]

>>> a.index(333)
1

>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]

>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]

>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]

5.1.1 ทําลิสตเปนสแต็ค (Using Lists as Stacks)

21 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ถาจะใชงานลิสตแบบสแต็คคือ เขาหลังออกกอน ก็แคใชเมธอดใหเหมาะสม คือเอาเขาดวย append() แลวเอา


ออกดวย pop()
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]

>>> stack.pop()
7

>>> stack
[3, 4, 5, 6]

>>> stack.pop()
6

>>> stack.pop()
5

>>> stack
[3, 4]

5.1.2 ใชงานลิสตแบบคิว (Using Lists as Queues)

คือ เขากอนออกกอน ก็ใช append() และ pop(0) ตามลําดับ


>>> queue = ["Eric", "John", "Michael"]
>>> queue.append("Terry") # Terry arrives
>>> queue.append("Graham") # Graham arrives
>>> queue.pop(0)
'Eric'

>>> queue.pop(0)
'John'

>>> queue
['Michael', 'Terry', 'Graham']

5.1.3 ฟงกชันที่ใชในการโปรแกรมแบบฟงกชัน (Functional Programming Tools)

มี 3 ตัว (ไมนับคําสั่ง lambda)

filter(function, sequence)
filter จะกรองขอมูลนําเขาใน sequence โดยเอาเฉพาะตัวที่ทําให function คืนคาจริง (ไมใชศูนยหรือ None)
ไว งงนิดหนอย ดูตัวอยางดีกวา เปนการหาจํานวนเฉพาะ
>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

map(function, sequence)
map จะเขาใจงายกวา คือจะเอาคาจาก sequence ไปทํางานใน function แลวคืนผลลัพธออกมาเปนลิสต
>>> def cube(x): return x*x*x
...
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

หาก function ตองการอารกิวเมนตมากกวาหนึ่งตัว ก็ตองใส sequence ดวยจํานวนที่เทากัน

22 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> seq = range(8)


>>> def add(x, y): return x+y
...
>>> map(add, seq, seq)
[0, 2, 4, 6, 8, 10, 12, 14]

>>> s2 = range(10,18)
>>> map(add, seq, s2)
[10, 12, 14, 16, 18, 20, 22, 24]

>>> s3 = range(18, 10, -1)


>>> map(add, seq, s3)
[18, 18, 18, 18, 18, 18, 18, 18]

reduce(function, sequence)
เอาขอมูลสองตัวแรกของ sequence ไปเรียก function ซึ่งเปนฟงกชันที่รับอารกิวเมนตสองตัว จากนั้นเอา
ผลลัพธที่ไดกับขอมูลถัดไปใน sequence ไปเรียก function ตอ ๆ ไปจนหมดขอมูล
>>> def add(x,y): return x+y
...
>>> reduce(add, range(1, 11))
55

ถาไมมีขอมูลจาก sequence จะแสดงขอผิดพลาด


>>> reduce(add, range(0))
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: reduce() of empty sequence with no initial value

เพื่อเปนการปองการการผิดพลาดดังกลาว อาจใสอารกิวเมนตตัวที่สามซึ่งจะกลายเปนคาเริ่มตนใหกับ
function เพื่อปองกันกรณีลิสตวาง ดังนี้

>>> def xsum(seq):


... def add(x,y): return x+y
... return reduce(add, seq, 0)
...

>>> xsum(range(1, 11))


55

>>> xsum([])
0

อารกิวเมนตที่สามของ reduce จะเปนคาเริ่มตน ซึ่ง reduce จะเริ่มคํานวณจากคาเริ่มตนและขอมูลแรกของ


sequence เปนคูแรก (แทนที่จะเปนขอมูลสองตัวแรกของ sequence) และถา sequence วางเปลา ก็จะคืนคา
เริ่มตนมาเลย

5.1.4 ลิสตจากลิสต (List Comprehensions)

เปนโครงสรางเฉพาะตัวของไพธอนที่ยืมมาจากภาษา Haskell/ML ในการสรางลิสตใหมจากลิสตที่มีอยู ใชมากใน


ไพธอน มีรูปแบบเปน
[f(x) for x in L [ if p(x) ] ]

แปลวา ใหสรางลิสตดวยฟงกชัน f จากลิสต L สมาชิกตอสมาชิก เฉพาะจากสมาชิกที่มีคา p(x) เปนจริง

โครงสรางสวนหลัง ตรงที่เปน if ... เปนสวนเสริม อาจใสหรือไมก็ได

ตัวอยาง

23 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']

>>> vec = [2, 4, 6]


>>> [3*x for x in vec]
[6, 12, 18]

>>> [3*x for x in vec if x > 3]


[12, 18]

>>> [3*x for x in vec if x < 2]


[]

>>> [[x,x**2] for x in vec]


[[2, 4], [4, 16], [6, 36]]

>>> [x, x**2 for x in vec] # error - parens required for tuples
File "<stdin>", line 1, in ?
[x, x**2 for x in vec]
^
SyntaxError: invalid syntax

>>> [(x, x**2) for x in vec]


[(2, 4), (4, 16), (6, 36)]

>>> vec1 = [2, 4, 6]


>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]

>>> [x+y for x in vec1 for y in vec2]


[6, 5, -7, 8, 7, -5, 10, 9, -3]

>>> [vec1[i]*vec2[i] for i in range(len(vec1))]


[8, 12, -54]

>>> [str(round(355/113.0, i)) for i in range(1,6)]


['3.1', '3.14', '3.142', '3.1416', '3.14159']

5.2 ประโยค del (The del statement)

ใชงานคลาย ๆ pop() แตเลือกชวงไดดวย จึงตองระบุดัชนีเสมอ


>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]

>>> del a[2:4]


>>> a
[1, 66.25, 1234.5]

>>> del a[:]


>>> a
[]

สามารถใช del ในการลบตัวแปรไดดวย


>>> del a

24 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

5.3 ทูเปล (Tuples and Sequences)

ทูเปลคลายกับลิสต ตางกันตรงเปนขอมูลที่สามารถกําหนดคาไดครั้งเดียว จึงเหมาะที่จะใชในงานที่ตองการคาคงที่

ลิสตใชวงเล็บกามปู [] แตทูเปลใชวงเล็บธรรมดา () หรืออาจละเลยไมใสก็ได โดยใสแคจุลภาค , ตามหลังสมาชิก


(แตตองระวังตัวเองงงเอง)
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345

>>> t
(12345, 54321, 'hello!')

>>> # Tuples may be nested:


... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))

ตัวอยางการละเลยการใสวงเล็บ ซึ่งถาดูผาน ๆ อาจสับสนได


>>> empty = ()
>>> singleton = 'hello', # <-- note trailing comma
>>> len(empty)
0

>>> len(singleton) # ไดคาเปน 1 เพราะเปนทูเปลที่มีสมาชิก 1 ตัว


1

>>> singleton
('hello',)

การละเลยการใสวงเล็บในตอนกําหนดคา ไพธอนเรียกวา การอัดขอมูลเปนอนุกรม (sequence packing) ในกรณีนี้


คือ tuple packing (ถาละเลยการใสวงเล็บแลว จะถือวาเปนขอมูล tuple เสมอ)
t = x, y, z

และยังสามารถกําหนดคาแบบยอนกลับได อันนี้เรียกวา การแตกขอมูลอนุกรม (sequence unpacking)


x, y, z = t

(ซึ่งถาเขียนใหถูกจริง ๆ แลวคือ (x, y, z) = t)

และแนนอนวาใชกับลิสตไดเชนเดียวกัน
[x, y, z] = t

5.4 เซ็ต (Sets)

ไพธอนรุนหลัง เติมความสามารถเรื่องเซ็ตเขาไป ซึ่งเซ็ตก็คือลิสตที่สามารถใชงานในลักษณะเซ็ตได เชน การทํายู


เนียนและอินเทอรเซกชัน เปนตน
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> fruit = set(basket) # create a set without duplicates
>>> fruit
set(['orange', 'pear', 'apple', 'banana'])

>>> 'orange' in fruit # fast membership testing

25 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

True

>>> 'crabgrass' in fruit


False

>>> # Demonstrate set operations on unique letters from two words


...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # unique letters in a
set(['a', 'r', 'b', 'c', 'd'])

>>> a - b # letters in a but not in b


set(['r', 'd', 'b'])

>>> a | b # letters in either a or b


set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])

>>> a & b # letters in both a and b


set(['a', 'c'])

>>> a ^ b # letters in a or b but not both


set(['r', 'd', 'b', 'm', 'z', 'l'])

5.5 ดิกชันนารี (Dictionaries)

เปนชนิดขอมูลพิเศษที่อยูในรูป {key: value, ...}

เปลี่ยนแปลงคาได
key อาจเปนขอมูลชนิดสตริง ตัวเลข หรือทูเปลที่ไมไดบรรจุ mutable object ไว
ลิสตใชเปนคียไมได เพราะมันเปลี่ยนคาภายในได
ลบคาภายในไดดวยประโยค del
ถาใสคาคียซ้ํา จะแทนที่คาเกา
ถาคนคียที่ไมมีอยู จะแสดงขอผิดพลาด
ใชเมธอด keys() ในการแสดงคาคียทั้งหมด และใชเมธอด has_key() ในการคนคาคีย
สมาชิกภายใน จะไมมีการเรียงลําดับตามคาของคีย
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}

>>> tel['jack']
4098

>>> del tel['sape']


>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}

>>> tel.keys()
['guido', 'irv', 'jack']

>>> tel.has_key('guido')
True

>>> 'guido' in tel


True

แปลงทูเปลในลิสตมาเปนดิกชันนารีดวยฟงกชัน dict()

26 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])


{'sape': 4139, 'jack': 4098, 'guido': 4127}

>>> dict([(x, x**2) for x in (2, 4, 6)]) # use a list comprehension


{2: 4, 4: 16, 6: 36}

หรือหากคาคียเปนสตริงลวน อาจกําหนดคาแบบนี้ก็ได (เลียนแบบการสงผานคาไปยังฟงกชัน)


>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}

5.6 เทคนิคการวนรอบ (Looping Techniques)

iteritems()
ใชแตกคาคูของดิกชันนารี
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.iteritems():
... print k, v
...
gallahad the pure
robin the brave

enumerate()
ใชแปลงจากลิสต (หรือทูเปล) มาเปนดิกชันนารี ที่มีคาคียเปนตัวเลข
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print i, v
...
0 tic
1 tac
2 toe

zip()
ใชจับคูสองอนุกรม (ลิสตหรือทูเปลหรือผสมกัน) ที่มีจํานวนสมาชิกเทากัน แปลงรูปมาใชงานแบบดิกชันนารี
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print 'What is your %s? It is %s.' % (q, a)
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.

reversed()
ใชกลับคาสมาชิก
>>> for i in reversed(xrange(1,10,2)):
... print i
...
9
7
5
3
1

sorted()
ใชจัดเรียงสมาชิก
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):

27 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

... print f
...
apple
banana
orange
pear

5.7 เงื่อนไข (More on Conditions)

เงื่อนไขในประโยค while และ if ไมจําเปนตองเปนการเปรียบเทียบคาเสมอไป แตจะเปนอะไรก็ไดที่ ถาคืน


คาที่ไมเปนศูนยหรือ None จะถูกนับวาเปนจริง
in และ not in ใชดูวามีคาอยูในลําดับขอมูลหรือเปลา
สําหรับลิสต is และ is not ใชดูวาเปนออบเจกตเดียวกันหรือเปลา
การเปรียบเทียบคา มีลําดับความสําคัญนอยกวาการกระทําทางคณิตศาสตร
การเปรียบเทียบคา สามารถเรียงตอกันในนิพจนเดียวได เชน a < b == c จะเปรียบเทียบวา a นอยกวา b และ
b เทากับ c หรือไม

>>> True == 1
True

>>> 1 < 2 == 1
False

>>> 1 < 2 == 2
True

ตัวกระทําทางตรรกะ มีความสําคัญนอยที่สุด โดย not สําคัญที่สุด และ or สําคัญนอยที่สุด เชน A and not B
or C มีความหมายเทากับ (A and (not B)) or C
ตัวกระทําทางตรรกะ จะกระทําจากซายไปขวา และถาจุดใดที่เมื่อคํานวณแลวสามารถระบุผลลัพธของทั้ง
นิพจนไดโดยไมตองเปรียบเทียบตอ ก็จะไมคํานวณนิพจนสวนที่เหลือ ไพธอนเรียกการนี้วา ตัวกระทําลัดวงจร
(short-circuit operators) และคาที่คืนออกมาจากการเปรียบเทียบ จะเปนคาสุดทายที่ทําการเปรียบเทียบ
สามารถกําหนดคาผลของการเปรียบเทียบ ใหกับตัวแปรได
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

5.8 น้ําหนักของขอมูลแบบลําดับ (Comparing Sequences and Other Types)

ออบเจกตแบบลําดับ สามารถนําไปเปรียบเทียบกับออบเจกตอื่นได โดยลิสตจะมีน้ําหนักนอยกวาสตริง สตริ


งนอยกวาทูเปล (อยาจํามาก อาจเปลี่ยนแปลงได ใหทดสอบตามรุนไพธอนที่ใชจริง)
การเปรียบเทียบยึดหลักจากซายไปขวา และหยุดทันทีพบความแตกตาง
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) < (1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)

6. มอดูล (Modules)

28 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

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 จึงไมตองกังวลเรื่องชื่อตัวแปร
ในมอดูลจะซ้ํากับตัวแปรในโปรแกรมหลัก
มอดูลสามารถอิมพอรตมอดูลอื่นได

29 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

เขียนรูปแบบการอิมพอรตไดหลากหลาย
เลือกอิมพอรตบางฟงกชัน
>>> 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

30 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> 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',

31 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

'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 ... ]


เรียกไดสองลักษณะ

32 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ถาไมมี 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)

แบบที่สองคือ 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

มีหลักอยูวา

ถาอยูในระดับชั้นไดเรกทอรีเดียวกัน เรียกไดโดยตรง โดยไมตองมีชื่อแพกเกจนําหนา เชนจากตัวอยาง มอดูล

33 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

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__ ซึ่งเปนลิสต เก็บคาพาธของมอดูลยอยไว

7. การอานและเขียนขอมูล (Input and Output)


7.1 การจัดรูปแบบเอาตพุต (Fancier Output Formatting)
7.2 การอานเขียนไฟล (Reading and Writing Files)

7.1 การจัดรูปแบบเอาตพุต (Fancier Output Formatting)

สามารถจัดรูปแบบไดสองแบบหลัก คือใชฟงกชัน และใชตัวกระทํา %

ตัวอยางการใชฟงกชันแปลงเปนตัวอักขระ str() ซึ่งใหคนอานงาย หรือแปลงแบบดิบ repr() คือใหระบบอาน


(สามารถเขียนอีกแบบภายใตเครื่องหมาย `...`)
>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'

>>> repr(s)
"'Hello, world.'"

>>> str(0.1)
'0.1'

>>> repr(0.1)
'0.10000000000000001'

>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
>>> print s
The value of x is 32.5, and y is 40000...

>>> # The repr() of a string adds string quotes and backslashes:


... hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print hellos

34 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

'hello, world\n'

>>> # The argument to repr() may be any Python object:


... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"

>>> # reverse quotes are convenient in interactive sessions:


... `x, y, ('spam', 'eggs')`
"(32.5, 40000, ('spam', 'eggs'))"

ตัวอยางการใชฟงกชัน repr() รวมกับเมธอดจัดชิดขวาของสตริง rjust() เทียบกับการใชตัวกระทําจัดรูปแบบ %


>>> for x in range(1, 11):
... print repr(x).rjust(2), repr(x*x).rjust(3),
... # Note trailing comma on previous line
... print repr(x*x*x).rjust(4)
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000

>>> for x in range(1,11):


... print '%2d %3d %4d' % (x, x*x, x*x*x)
...
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000

ลองดูเมธอด zfill() บาง


>>> '12'.zfill(5)
'00012'

>>> '-3.14'.zfill(7)
'-003.14'

>>> '3.14159265359'.zfill(5)
'3.14159265359'

เทียบกับการใช %
>>> import math
>>> print 'The value of PI is approximately %5.3f.' % math.pi
The value of PI is approximately 3.142.

อีกอันนึงเปนการจัดชิดขอบซายขวาดวย %
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
... print '%-10s ==> %10d' % (name, phone)
...
Jack ==> 4098

35 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

Dcab ==> 7678


Sjoerd ==> 4127

พิเศษนิดนึงสําหรับตัวจัดรูปสตริง %s คือถาขอมูลไมอยูในรูปสตริง เขาจะแปลงอัตโนมัติดวยฟงกชัน str()


>>> print "%s %s %s" % (1, True, None)
1 True None

พิเศษกวานั้น ถาขอมูลเปนดิกชันนารี ยังใชรูปแบบ %(name)format ไดอีกดวย


>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % table
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

7.2 การอานเขียนไฟล (Reading and Writing Files)

เปดไฟลดวยฟงกชัน open(filename, mode) ไดคาเปน ไฟลออบเจกต


>>> f=open('/tmp/workfile', 'w')
>>> print f
<open file '/tmp/workfile', mode 'w' at 80a0960>

mode มีคาไดดังนี้

'r' อานอยางเดียว ตัวนี้เปนคาปริยาย


'w' เขียนอยางเดียว ถามีเนื้อเกาจะถูกทับหมด
'a' เติมอยางเดียว คือเขียนตอที่ทายไฟล
'r+' ทั้งอานและเขียน (ใชรวมกับเมธอด seek() ในการเลื่อนตําแหนงอานเขียน)

สําหรับเครื่องวินโดวสและแมคอินทอช จะมีการเปดแบบไบนารีดวย ดังนั้นจะมีโหมดเพิ่มคือ 'rb' 'wb' และ 'r+b'

การจัดการไฟลระหวางการเปดแบบอักขระกับการเปดแบบไบนารีคือ ในการเปดแบบอักขระ ระบบจะเติมการจบ


บรรทัดดวยอักขระพิเศษเพิ่มเขาไป ทําใหเกิดปญหาถาไฟลนั้นเปนไฟลไบนารี

7.2.1 เมธอดที่ใช (Methods of File Objects)

f.read([ size ])
ถาไมระบุขนาดไบตหรือขนาดเปนลบ เขาจะอานทั้งไฟล ถาอานไฟลจนหมดแลว จะคืนคาเปนอักขระวาง ("")
>>> f.read()
'This is the entire file.\n'

>>> f.read()
''

f.readline()
อานหนึ่งบรรทัด คืออานจนพบอักขระ \n ถาอานไฟลจนหมดแลว จะคืนคาเปนอักขระวาง ("")
>>> f.readline()
'This is the first line of the file.\n'

>>> f.readline()
'Second line of the file\n'

>>> f.readline()
''

f.readlines()

36 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

อานทั้งไฟลโดยแยกแตละบรรทัดเปนแตละสมาชิกในลิสต
>>> f.readlines()
['This is the first line of the file.\n', 'Second line of the file\n']

for line in f:
อันนี้ไมใชเมธอด แตเปนประโยคการเขียนแบบพิเศษ ที่ใชเปนปกติในไพธอน อานโคดงาย และเหมาะสมใน
การใชงานจริง
>>> for line in f:
print line,
This is the first line of the file.
Second line of the file

f.write(string)
เขียนสตริงกลงไฟล คืนคาเปน None
>>> f.write('This is a test\n')

ถาขอมูลไมไดอยูในรูปสตริง ตองแปลงกอน
>>> value = ('the answer', 42)
>>> s = str(value)
>>> f.write(s)

f.tell()
ใชบอกตําแหนงปจจุบันของไฟล นับจากตนไฟล
f.seek(offset [, from_what])
ใชตั้งตําแหนงที่จะอานเขียนไฟล offset คือจํานวนไบต from_what คือตําแหนงอางอิง
0 หรือ ไมใส คือเทียบจากตนไฟล
1 คือ จากตําแหนงปจจุบัน
2 คือ จากทายไฟล
>>> f = open('/tmp/workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5) # Go to the 6th byte in the file
>>> f.read(1)
'5'

>>> f.seek(-3, 2) # Go to the 3rd byte before the end


>>> f.read(1)
'd'

f.close()
ปดการทํางานกับไฟลนั้น ถาปดแลวกลับมาอานอีกจะเกิดขอผิดพลาด
>>> f.close()
>>> f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file

7.2.2 ปกเกิล (The pickle Module)

เปนการเก็บออบเจกตลงไฟลแบบไรขอจํากัด ดูเรื่องปกเกิลในบรรณสารของไพธอน

ขั้นตอนการทํางานคือ ไพธอนจะแปลงออบเจกตเปนสตริงกอน เรียกวาปกกลิง (pickling) เวลานํากลับจะแปลงสตริ


งนั้นกลับเปนออบเจกตอีกที เรียกวา อันปกกลิง (unpickling)

ตัวอยางการใชงาน สมมุติวามีออบเจกต x ที่เราตองการเก็บ ลงไวในไฟลที่เปดไวแลว คือไฟลออบเจกต f รูปแบบ

37 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

คือ
pickle.dump(x, f)

เวลานํากลับก็ใชวา (f ตองถูกเปดอยูกอนแลว)
x = pickle.load(f)

8. ขอผิดพลาดและสิ่งผิดปรกติ (Errors and


Exceptions)
8.1 ขอผิดพลาดทางโครงสรางประโยค (Syntax Errors)
8.2 สิ่งผิดปรกติตอนทํางาน (Exceptions)
8.3 การจัดการสิ่งผิดปรกติ (Handling Exceptions)
8.4 การปาวสิ่งผิดปรกติ (Raising Exceptions)
8.5 สรางชนิดสิ่งผิดปรกติเอง (User-defined Exceptions)
8.6 เก็บกวาดปดทาย (Defining Clean-up Actions)
8.7 การเก็บกวาดที่มีไวให (Predefined Clean-up Actions)

ขอผิดพลาดมีสองแบบคือ ผิดทางโครงสรางประโยค (syntax errors) หรือเกิดสิ่งผิดปรกติตอนโปรแกรมทํางาน


(exceptions)

8.1 ขอผิดพลาดทางโครงสรางประโยค (Syntax Errors)

มือใหมตองเจอทุกคน
>>> while True print 'Hello world'
File "<stdin>", line 1, in ?
while True print 'Hello world'
^
SyntaxError: invalid syntax

ในที่นี้คือตก : หลัง True

ขอผิดพลาดแบบนี้ตองตามไปแกในโคดอยางเดียว

8.2 สิ่งผิดปรกติตอนทํางาน (Exceptions)

เกิดตอนรัน
>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero

>>> 4 + spam*3
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined

>>> '2' + 2

38 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

Traceback (most recent call last):


File "<stdin>", line 1, in ?
TypeError: cannot concatenate 'str' and 'int' objects

บรรทัดสุดทายเปนการบอกวาผิดเรื่องอะไร

จากตัวอยาง จะเห็นวาผิดอยูสามเรื่องคือ ZeroDivisionError, NameError และ TypeError สามารถดูรายละเอียด


ทั้งหมดไดที่ module-exceptions

8.3 การจัดการสิ่งผิดปรกติ (Handling Exceptions)

จัดการดวยประโยค try: ... except[(ErrorType)]: ... [else: ...]

ตัวอยางขางลางเปนการจัดการสิ่งผิดปรกติ โดยจะจัดการดูเฉพาะคาที่เราปอนเขาไปวาถูกตองหรือไม
(ValueError) แตถาเปนสิ่งผิดปรกติแบบอื่น โปรแกรมจะปลอยใหเปนหนาที่ของระบบ จึงทําใหเราสามารถใชปุม
Ctrl-C ในการตัดการทํางานของโปรแกรมได ซึ่งกรณีตัดการทํางานนี้จะเปนสิ่งผิดปรกติแบบ KeyboardInterrupt

>>> while True:


... try:
... x = int(raw_input("Please enter a number: "))
... break
... except ValueError:
... print "Oops! That was no valid number. Try again..."
...

เราระบุสิ่งผิดปรกติไดหลายแบบในครั้งเดียว
... except (RuntimeError, TypeError, NameError):
... pass

หรืออาจดักเปนขั้น ๆ จนในที่สุดไมใสอะไรเลย คือ ดักทุกอยางที่เหลือ


import sys

try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except IOError, (errno, strerror):
print "I/O error(%s): %s" % (errno, strerror)
except ValueError:
print "Could not convert data to an integer."
except:
print "Unexpected error:", sys.exc_info()[0]
raise

อาจใชรวมกับ else: ในกรณีที่ try: ไมพบสิ่งผิดปรกติ


for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except IOError:
print 'cannot open', arg
else:
print arg, 'has', len(f.readlines()), 'lines'
f.close()

ถาเติมตัวแปร (อาจเปนทูเปล) หลัง Exception ตัวแปรนั้นจะเก็บคาอินสแตนซของการดัก ซึ่งสามารถนําอารกิ


วเมนตมาแสดงผลได

39 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> try:
... raise Exception('spam', 'eggs')
... except Exception, inst:
... print type(inst) # the exception instance
... print inst.args # arguments stored in .args
... print inst # __str__ allows args to printed directly
... x, y = inst # __getitem__ allows args to be unpacked directly
... print 'x =', x
... print 'y =', y
...
<type 'instance'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

การดักสิ่งผิดปรกตินี้ ไมเพียงแตดักเฉพาะโคดในบล็อคนั้น แตสามารถดักไปถึงฟงกชันที่ถูกเรียกดวย


>>> def this_fails():
... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError, detail:
... print 'Handling run-time error:', detail
...
Handling run-time error: integer division or modulo by zero

8.4 การปาวสิ่งผิดปรกติ (Raising Exceptions)

เราสามารถปาวสิ่งผิดปรกติเมื่อพบได เชน
>>> raise NameError, 'HiThere'
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: HiThere

พารามิเตอรตัวแรกเปนชื่อสิ่งผิดปรกติ อันหลังเปนขอความประกอบ ซึ่งอาจเขียนอีกรูปนึงวา raise


NameError('HiThere')

สามารถใชคําสั่ง raise เพื่อปาวสิ่งผิดปรกติตอภายในบล็อคที่ดักสิ่งผิดปรกติได เพื่อใหโคดผูเรียกจัดการตอ


>>> try:
... raise NameError, 'HiThere'
... except NameError:
... print 'An exception flew by!'
... raise
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere

8.5 สรางชนิดสิ่งผิดปรกติเอง (User-defined Exceptions)

เราอาจสรางชนิดสิ่งผิดปรกติเองโดยสรางเปนคลาส ซึ่งตองผัน (derive) มาจากคลาส Exception ไมวาจะโดยตรง


หรือโดยออม

40 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> class MyError(Exception):


... def __init__(self, value):
... self.value = value
... def __str__(self):
... return repr(self.value)
...
>>> try:
... raise MyError(2*2)
... except MyError, e:
... print 'My exception occurred, value:', e.value
...
My exception occurred, value: 4

>>> raise MyError, 'oops!'


Traceback (most recent call last):
File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'

จากตัวอยางนี้ เมธอด __init__ ของคลาสเริ่มคือ Exception ถูกครอบดวยโคดของเรา

หากมอดูลเราตองมีการดักสิ่งผิดปรกติหลายอยาง เราควรสรางคลาสที่ใชเปนฐานกอน แลวจึงผันจากคลาสฐานของ


เราอีกทีนึง เวลาเปลี่ยนแปลงอะไรจะทําไดงายกวา คือทําที่คลาสฐานอันเดียว
class Error(Exception):
"""Base class for exceptions in this module."""
pass

class InputError(Error):
"""Exception raised for errors in the input.

Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""

def __init__(self, expression, message):


self.expression = expression
self.message = message

class TransitionError(Error):
"""Raised when an operation attempts a state transition that's not
allowed.

Attributes:
previous -- state at beginning of transition
next -- attempted new state
message -- explanation of why the specific transition is not allowed
"""

def __init__(self, previous, next, message):


self.previous = previous
self.next = next
self.message = message

ดูเรื่องคลาสไดในบทตอไป

8.6 เก็บกวาดปดทาย (Defining Clean-up Actions)

ในประโยค try: จะมีประโยคยอยสวนเสริมอีกตัวคือ finally: โคดที่อยูในบล็อคนี้จะถูกรันเสมอ ไมวาจะเกิดสิ่งผิด


ปรกติขึ้นหรือไม
>>> try:

41 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

... raise KeyboardInterrupt


... finally:
... print 'Goodbye, world!'
...
Goodbye, world!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
KeyboardInterrupt

ประโยชนของประโยคยอย finally: คือ ถาผานบล็อคของ except: และ else: มาแลว ถายังไมแจงสิ่งผิดปรกติ


จะถูกแจงในบล็อคนี้ และไมวาจะพบคําสั่ง break continue หรือ return ในบล็อคดังกลาวก็ตาม โคดในสวนนี้ก็ยัง
จะถูกรันเสมอ
>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print "division by zero!"
... else:
... print "result is", result
... finally:
... print "executing finally clause"
...

>>> divide(2, 1)
result is 2
executing finally clause

>>> divide(2, 0)
division by zero!
executing finally clause

>>> divide("2", "1")


executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

8.7 การเก็บกวาดที่มีไวให (Predefined Clean-up Actions)

ออบเจกตบางชนิดจะเตรียมการเก็บกวาดไวให เมื่อออบเจกตเลิกใชแลว ไมวาโคดที่ใชออบเจกตจะทํางานสําเร็จ


หรือลมเหลว ดูตัวอยางการเปดไฟลตอไปนี้
for line in open("myfile.txt"):
print line

ปญหาของโคดนี้คือ ไมมีการปดไฟล ถาโปรแกรมใหญขึ้นโคดแบบนี้จะกินหนวยความจํามหาศาล ออบเจกตประเภท


ไฟลจึงเตรียมการเก็บกวาดไวให โดยสามารถใชไดผานประโยค with ดังนี้
with open("myfile.txt") as f:
for line in f:
print line

ตัวอยางนี้ ไฟลออบเจกต f จะถูกลบออกจากหนวยความจําเมื่อโปรแกรมจบ

9. คลาส (Classes)

42 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

9.1 ศัพท (A Word About Terminology)


9.2 สโคปและเนมสเปซ (Python Scopes and Name Spaces)
9.3 แรกพบคลาส (A First Look at Classes)
9.4 หมายเหตุเรื่องคลาส (Random Remarks)
9.5 การสืบทอดคลาส (Inheritance)
9.6 ตัวแปรเฉพาะที่ (Private Variables)
9.7 ปกิณกะ (Odds and Ends)
9.8 ตัวยกขอผิดพลาดก็เปนคลาส (Exceptions Are Classes Too)
9.9 ตัวกระทําซ้ํา (Iterators)
9.10 เจนเนอเรเตอร (Generators)
9.11 เจนเนอเรเตอรเอกซเพรสชั่น (Generator Expressions)

คลาสในไพธอนถูกออกแบบมาใหใชงานงาย มันเลยไมไดปองกันแนนหนาแบบภาษาอื่น อยางไรก็ตามมันก็ยังมี


คุณสมบัติของคลาสอยางที่ควรเปน

9.1 ศัพท (A Word About Terminology)

เพื่อใหเกิดความสุขสวัสดีทั้งผูเขียนและผูอาน บทนี้จะใชทับศัพทใหมากที่สุดเทาที่เปนไปได :)

9.2 สโคปและเนมสเปซ (Python Scopes and Name Spaces)

ศัพทสนุก ๆ

เนมสเปซ (Namespace)
เปนรายชื่อของออบเจกตทั้งหมดในขอบเขตหนึ่ง ๆ เนมสเปซมีหลายระดับ ตั้งแตของทั้งโปรแกรมมาจนถึงเนม
สเปซภายในออบเจกต ในทางปฏิบัติแลวไพธอนเก็บไวในรูปดิกชันนารี ตัวอยางของเนมสเปซคือ กลุมของชื่อ
บิลตอิน (Built-in Names = built-in function + built-in exception) จะอยูภายใตเนมสเปซเดียวกัน หรือ ชื่อสวน
รวม (Global Names = global function + global exception + global variables) ตาง ๆ ของมอดูล ก็จะอยูในเนม
สเปซของมัน การมีเนมสเปซทําใหออบเจกตตางเนมสเปซกันไมตีกันถึงแมจะชื่อเดียวกัน เวลาเราอางถึงออ
บเจกตที่อยูลึกลงไป เราจึงตองอางตามรายแอตทริบิวต

เนมสเปซตาง ๆ จะถูกสรางขึ้นเมื่อออบเจกตถูกสราง และมีอายุตามออบเจกตนั้น ๆ เชน

เนมสเปซที่บรรจุชื่อบิลตอิน (Built-in Name = built-in function + built-in exception) จะถูกสรางขึ้น


ตั้งแตเราเริ่มเรียกใชโปรแกรมและคงอยูจนกวาโปรแกรมจะจบ
เนมสเปซสวนรวมของมอดูล (Module Global Namespace) จะถูกสรางขึ้นตั้งแตมอดูลถูกอิมพอรตจน
กวาจะจบโปรแกรมเชนกัน
ประโยคทั้งหมดที่ถูกรันโดยไพธอน ไมวาจะเปนการรันสคริปตไฟลหรือพิมพสดในโหมดโตตอบก็ดี จะ
อยูภายใตเนมสเปซของมอดูล __main__
ชื่อบิลตอิน (Built-in Names = built-in function + built-in exception) จะอยูภายใตเนมสเปซของมอดูล
__builtin__
เวลาฟงกชันถูกเรียกใชงาน มันจะสรางเนมสเปซของมันขึ้นมา แลวถูกลบทิ้งไปเมื่อทํางานเสร็จ การส
รางเนมสเปซจะเกิดทุกครั้งที่เรียกฟงกชัน แมในการเรียกตัวเองของฟงกชัน

อธิบายตามภาษาชาวบาน ชื่อ (Names) ก็คืออะไรที่เราตองตั้งชื่อใหมัน เวลาเรียกก็เรียกจากชื่อ เชนฟงกชัน


ตัวแปร เปนตน สวนเนมสเปซ (Namespace) ก็คือหองบรรจุชื่อนั่นเอง

43 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

แอตทริบิวต (Attribute)
ก็คือชื่อที่อยูภายในเนมสเปซหนึ่ง ๆ อางดวยเนมสเปซ ตามดวยจุด (.) ตามดวยชื่อแอตทริบิวต เชน z.real
เราเรียก real วาเปนแอตทริบิวตของออบเจกต z อะไรก็ตามที่อยูในระดับเดียวกับ real คืออางถึงดวย z.XXX
เราจะเรียกวาอยูภายใตเนมสเปซเดียวกัน (ถาชื่อซ้ําก็ตีกัน)

แอตทริบิวต อาจเปนไดทั้งอานอยางเดียวและเขียนไดดวย ถาเปนแบบเขียนได เราก็สามารถเปลี่ยนแปลงคา


ได และใชประโยค del ในการลบแอตทริบิวตนั้นได

สโคป (Scope)
หมายถึงชวงที่เนมสเปซสามารถเขาถึงไดโดยตรง โดยไมตองอางอิงไลแอตทริบิวตไปถึงออบเจกตที่อยูใน
เนมสเปซอื่น

ในทุก ๆ ขณะของการทํางาน จะมีอยางนอยสามสโคปเสมอ คือ

สโคปในสุด (ซึ่งจะถูกคนกอน) บรรจุตัวแปรทองถิ่นของฟงกชัน และอาจมีเนมสเปซของฟงกชันที่


ครอบอยู ในกรณีที่มีการกําหนดฟงกชันซอนในฟงกชัน
สโคปชั้นกลาง บรรจุชื่อตัวแปรและฟงกชันสวนรวมของมอดูล
สโคปชั้นนอกสุด บรรจุพวกชื่อบิลติอิน (Built-in Names = built-in function + built-in exception)

เรื่องสนุก ๆ

ถาเรากําหนดชื่อ (Name) ของเราใหเปนชื่อสวนรวม (global) ทุกออบเจกตจะเห็นในสโคปชั้นกลางเสมอ


ตัวแปรอื่น ๆ ที่อยูนอกสโคปในสุดที่ไมไดประกาศใหเปนชื่อสวนรวม จะเปนตัวแปรที่อานไดอยางเดียว (ถา
พยายามจะเขียนทับ จะถือเปนการสรางตัวแปรใหมในเนมสเปซชั้นในสุด)
โดยปกติแลว สโคปทองถิ่น (local scope) จะอางถึงชื่อตาง ๆ ที่อยูในฟงกชันปจจุบัน ถาอยูนอกฟงกชันแลว
สโคปทองถิ่นจะอางเนมสเปซเดียวกับสโคปสวนรวม (global scope) สวนการนิยามคลาส จะเปนการสรางเนม
สเปซครอบทับสิ่งที่อยูในคลาสอีกชั้นหนึ่ง

--รอแปล--

9.3 แรกพบคลาส (A First Look at Classes)

9.3.1 โครงสรางคลาส (Class Definition Syntax)

โครงคือ
class ClassName:
<statement-1>
.
.
.
<statement-N>

--รอแปล--

9.3.2 ออบเจกตที่เปนคลาส (Class Objects)

จะใชงานหรือเขาถึงคลาสออบเจกตไดสองแบบ คือ

เขาถึงแบบอางตามแอตทริบิวต (Attribute references)


ดูโคด

44 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

class MyClass:
"A simple example class"
i = 12345
def f(self):
return 'hello world'

เราสามารถเขาถึงคลาสนี้ดังนี้
>>> MyClass.i
12345

>>> MyClass.f
<unbound method MyClass.f>

>>> MyClass.__doc__
'A simple example class'

>>> MyClass.i = 2
>>> MyClass.i
2

>>> MyClass.__doc__ = "Modified docstring"


>>> MyClass.__doc__
'Modified docstring'

การสรางอินสแตนซ (instantiation)
ใชรูปแบบธรรมชาติเหมือนกับการสรางฟงกชัน
x = MyClass()

เปนการสรางอินสแตนซซึ่งเปนออบเจกตที่ถูกบรรจุอยูในตัวแปร x

หากตองการออบเจกตที่ตองมีการถูกเตรียมการในครั้งแรก ตองใสเมธอดพิเศษชื่อ __init__() ลงในการนิยามคลา


สดวย
def __init__(self):
self.data = []

พอสรางออบเจกตแลว เราจะไดผลของการรันเมธอด __init__() มาดวย เชน


>>> x=MyClass()
>>> x.data
[]

ถาตองการใสพารามิเตอรใหกับคลาส ก็ตองใสในเมธอด __init__() นี้เอง เชน


>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)

9.3.3 ออบเจกตที่เปนอินสแตนซ (Instance Objects)

นิยามวามันมีแอตทริบิวตสองแบบ คือ แอตทริบิวตที่เปนขอมูล และ เมธอด

แอตทริบิวตที่เปนขอมูล (Data Attributes)


ก็คือตัวแปรของอินแสตนซออบเจกตนั่นเอง ดังนั้นจึงสามารถใชงานแบบตัวแปร คือกําหนดคาไดทันที จาก
ตัวอยางขางลางนี้ จะไดผลลัพธคือ 16

45 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

x.counter = 1
while x.counter < 10:
x.counter = x.counter * 2
print x.counter
del x.counter

เมธอด (Method)
ก็คือฟงกชันของอินสแตนซออบเจกตนั่นเอง จากตัวอยาง x.f คือเมธอดของอินสแตนซ สวน MyClass.f คือ
ฟงกชันของคลาส

9.3.4 ออบเจกตที่เปนเมธอด (Method Objects)

จากตัวอยางคือ x.f() เรียกวาเปนเมธอด

เราอาจอางถึงเมธอดผานตัวแปรได
xf = x.f
while True:
print xf()

ตัวอยางนี้จะพิมพ "hello world" ไปเรื่อย จนกวาจะกดขัดจังหวะ

เมธอดสามารถใชงานพารามิเตอรไดเหมือนฟงกชันปกติ เวลาเรียกใชงานก็ตองใสพารามิเตอรใหครบเชนกัน ไมงั้น


ไพธอนจะยกขอผิดพลาดขึ้นแสดง

แตพารามิเตอร (arguments) ของเมธอดจะตางไปจากฟงกชันปกติเล็กนอย เพราะเมธอดเปนฟงกชันของอินสแตนซ


ของคลาส ดังนั้นพฤติกรรมของเมธอดคือ เมื่อเราเรียกใชเมธอดวา x.f() จริง ๆ แลวมันคือการที่เราเรียกวา
MyClass.f(x) นี่คือเหตุที่ตองกําหนดตัว แปรพิเศษ self ในตอนนิยามคลาส

9.4 หมายเหตุเรื่องคลาส (Random Remarks)

แอตทริบิวตขอมูลจะสําคัญกวาแอตทริบิวตที่เปนเมธอดเสมอ แปลวาถาตั้งชื่อตัวแปรกับฟงกชันซ้ํากัน ชื่อตัว


แปรจะสําคัญกวา ดังนั้นเราจึงควรตระหนักและหาทางปองกันขอผิดพลาดนี้ อาจจะดวยการตั้งชื่อเมธอดให
เปนอักษรตัวใหญนําหนา หรือนําหนาตัวแปรดวยเครื่องหมาย "_" หรืออื่น ๆ ที่เคยชิน
ไพธอนไมไดปกปองตัวแปรในอินสแตนซอยางสมบูรณแบบ ใคร ๆ ก็สามารถเปลี่ยนคามันได กลาวคือ ไมรอง
รับการซอนขอมูล (data hiding) แตอยางใด หากตองการคุณสมบัตินี้จริง ๆ อาจตองเขียนสวนขยายดวยภาษา
ซี
เปนหนาที่ของเราเองที่ตองใชแอตทริบิวตขอมูลดวยความระมัดระวัง ควรตั้งขอกําหนดใหแมน ในเรื่องการตั้ง
ชื่อ อาจลดความผิดพลาดได
ไมสามารถอางถึงแอตทริบิวตของออบเจกตแบบสั้นจากภายในตัวเมธอดได ไมวาจะเปนแอตทริบิวตขอมูล
หรือแอตทริบิวตที่เปนเมธอด ซึ่งปรากฏวาทําใหอานโคดงาย คือจะไมเกิดความสับสนระหวางแอตทริบิวต
ขอมูลกับตัวแปรทองถิ่น
อารกิวเมนตตัวแรกของเมธอดมักชื่อ self แตก็ไมใชขอบังคับอะไร เปนเพียงขอตกลงเทานั้น แตใชดีกวาไม
ใช เพราะเพื่อใหเปนนิสัยแหงการทําตามมาตรฐาน มีผลใหคนอื่นอานโคดเรางายขึ้น
เนื่องจากไพธอนแทนคาตัวแปรไดอยางไมมีขอจํากัด ดังนั้นจึงสามารถเขียนโคดใหเอาฟงกชันจากภายนอก
มาเปนฟงกชันภายในคลาสได แตอันนี้เปนตัวอยางที่ไมดีมาก ๆ เพราะอานโคดแลวสับสน
# Function defined outside the class
def f1(self, x, y):
return min(x, x+y)

class C:
f = f1
def g(self):

46 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

return 'hello world'


h = g

คลาส C สามารถเรียกใชงานเมธอดแอตทริบิวต C.f(x,y) ได


>>> x = C()
>>> x.f(1,2)
1

เมธอดอาจเรียกใชเมธอดอื่นในคลาสเดียวกันไดดวยการใช self นําหนาเมธอดที่จะเรียก


class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)

9.5 การสืบทอดคลาส (Inheritance)

ถาสืบทอดไมไดก็ไมใชคลาส รูปแบบโครงสรางของการสืบทอดคือ
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>

ถาคลาสฐานถูกกําหนดไวที่มอดูลอื่น รูปแบบจะเปน
class DerivedClassName(modname.BaseClassName):

คลาสใหมที่แตกออกมานี้ สามารถสรางเมธอดเพื่อครอบงําเมธอดเดิมไดอยางไมมีขอจํากัด โดยที่ถาสรางขึ้นมาแลว


เมื่อมีการเรียกเมธอดจะเรียกไปยังเมธอดใหมแทน โคดภายใตเมธอดเดิมจะไมถูกเรียก แตหากยังตองการเรียกโคด
จากเมธอดเดิมอยู เราตองเรียกใชเองในรูปแบบ BaseClassName.methodname(self, arguments)

9.5.1 การสืบทอดหลายทาง (Multiple Inheritance)

รูปแบบโครงสรางคือ
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>

ลําดับการคนหาแอตทริบิวตในอินสแตนซของ DerivedClassName ก็คือ ถาพบแอตทริบิวตใน DerivedClassName


ก็จะใชเลย แตถาไมพบก็จะหาใน Base1 และคลาสฐานทั้งหมดของ Base1 ลึกลงไปจนสุด และถายังไมพบจึงมาเริ่ม
ตนคนจาก Base2 ตอไปเรื่อย ๆ จนหมด

(บางคนอาจคิดวานาจะไลไป Base1 - Base2 - Base3 แลวจึงยอนมาหาฐานของ Base1 อีกที แตการไลแบบนั้นจะทํา


ใหคุณตองแยกแยะกอน วาแอตทริบิวตเจาปญหานั้นกําหนดไวใน Base1 หรือคลาสฐานของ Base1 กอนที่จะตรวจ
สอบวาชนกับชื่อใน Base2 หรือไม ซึ่งเทากับเปนการตัดทอนสายตระกูลตามปกติของคลาสออกเปนสวน ๆ ในขณะที่
การคนหาแบบลงลึกทีละสายจะไมมีความแตกตางตรงนี้)

47 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

9.6 ตัวแปรสวนตัว (Private Variables)

ใชหลักแควานําหนาชื่อตัวแปรดวย underscore สองตัว เชน __spam ตัวแปรนั้นจะกลายเปนตัวแปรสวนตัวของคลา


สนั้นเอง ไมสามารถถูกเรียกจากที่อื่นในรูป X.__spam ได
>>> class C:
... __spam = 5
... s = 6
...
>>> a = C()
>>> a.s
6

>>> a.__spam
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: C instance has no attribute '__spam'

แตไพธอนก็ยังไมทําใหเปนสวนตัวจริง ๆ อยูดี เพราะอาจถูกเรียกในรูปของ X._classname__spam ได


>>> a._C__spam
5

>>> dir(a)
['_C__spam', '__doc__', '__module__', 's']

9.7 ปกิณกะ (Odds and Ends)

อาจเติมแอตทริบิวตขอมูลใหกับคลาสอินสแตนซไดทุกเมื่อ
class Employee:
pass

john = Employee() # Create an empty employee record

# Fill the fields of the record


john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

9.8 ตัวยกขอผิดพลาดก็เปนคลาส (Exceptions Are Classes Too)

ดังนั้น เราสามารถสรางตัวยกขอผิดพลาดแบบซอนลึกลงไปเรื่อย ๆ
เขียนไดสองรูปแบบคือ
raise Class, instance

instance ในที่นี้ คืออินสแตนซของ Class หรือคลาสลูก

อีกอันคือ
raise instance

ซึ่งถาเขียนแบบเต็ม ๆ ตองเขียนวา

48 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

raise instance.__class__, instance

แตตองระวังการดักตอนแตกลูกแตกหลานคลาส เพราะถาดักพบคลาสแมกอน เขาจะถือวาดักไดแลว และจะเอาขึ้น


เลย ลองดู
>>> class B:
... pass
...
>>> class C(B):
... pass
...
>>> class D(C):
... pass
...
>>> for c in [B, C, D]:
... try:
... raise c()
... except D:
... print "D"
... except C:
... print "C"
... except B:
... print "B"
...
B
C
D

>>> for c in [B, C, D]:


... try:
... raise c()
... except C:
... print "C"
... except B:
... print "B"
... except D:
... print "D"
...
B
C
C

เวลายกขอผิดพลาดขึ้นแสดง รูปแบบคือ
Exception_Class: str(instance)

9.9 ตัวกระทําซ้ํา (Iterators)

ลองดูตัวอยาง for
for element in [1, 2, 3]:
print element
for element in (1, 2, 3):
print element
for key in {'one':1, 'two':2}:
print key
for char in "123":
print char
for line in open("myfile.txt"):
print line

เบื้องหนาก็ดูงาย ๆ ดี เราลองมาดูเบื้องลึกบาง

49 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ขั้นตอนคือ เมื่อไพธอนพบคําสั่ง for เขาจะไปเรียกเมธอด iter() ของออบเจกตนั้น ซึ่งจะคืนคาเปนออบเจกตที่มี


เมธอด next() ออกมา และจะเรียกซ้ําไปเรื่อย จนเมื่อหมดแลว เมธอด next() จะยกขอผิดพลาดชื่อ
StopIteration ขึ้นมาบอกใหรูวา พอแลว ลองดูตัวอยาง

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> it.next()
'a'

>>> it.next()
'b'

>>> it.next()
'c'

>>> it.next()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
it.next()
StopIteration

เมื่อรูเบื้องลึกแลว เราก็สามารถแปลงพฤติกรรมของการทําซ้ําได ดวยการนิยามเมธอด __iter__() และ next() ใน


คลาสของเราใหม
class Reverse:
"Iterator for looping over a sequence backwards"
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]

รันไดวา
>>> for char in Reverse('spam'):
... print char
...
m
a
p
s

9.10 เจนเนอเรเตอร (Generators)

เปนอีกตัวนึงที่ใชสรางตัวกระทําซ้ํา โดยใชประโยค yield ซึ่งพิเศษตรงที่วามันสามารถจําขอมูลและสถานะจากครั้ง


กอนที่เคยรันได พอถูกเรียกจาก next() เมื่อไหร มันจะกลับไปทํางานดวยสถานะจากครั้งกอนทันที
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]

รันไดวา
>>> for char in reverse('golf'):

50 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

... print char


...
f
l
o
g

ทําใหเขียนโคดไดสั้น แตอาจอานยากนิดนึง แตถาใชคลองแลวจะประหยัดโคดไปไดเยอะ เพราะไมตองมานั่งเขียน


พวก self.index และ self.data เอง

9.11 เจนเนอเรเตอรเอกซเพรสชั่น (Generator Expressions)

รูปแบบเหมือน ลิสตคอมพรีเฮนชั่น (list comprehension) แตใชวงเล็บธรรมดาแทน เขียนและอานโคดงาย และ


ประหยัดหนวยความจํา
>>> sum(i*i for i in range(10)) # sum of squares
285

>>> xvec = [10, 20, 30]


>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec)) # dot product
260

>>> from math import pi, sin


>>> sine_table = dict((x, sin(x*pi/180)) for x in range(0, 91))

>>> unique_words = set(word for line in page for word in line.split())

>>> valedictorian = max((student.gpa, student.name) for student in graduates)

>>> data = 'golf'


>>> list(data[i] for i in range(len(data)-1,-1,-1))
['f', 'l', 'o', 'g']

10. มีอะไรใหใชบาง (Brief Tour of the Standard


Library)
10.1 งานของระบบปฏิบัติการ (Operating System Interface)
10.2 ไฟลไวลดคารด (File Wildcards)
10.3 อารกิวเมนต (Command Line Arguments)
10.4 นําเขา สงออก และเปลี่ยนทิศ (Error Output Redirection and Program Termination)
10.5 จับคูสตริง (String Pattern Matching)
10.6 คณิตศาสตร (Mathematics)
10.7 อินเทอรเน็ต (Internet Access)
10.8 วันที่และเวลา (Dates and Times)
10.9 การบีบอัดขอมูล (Data Compression)
10.10 จับประสิทธิภาพ (Performance Measurement)
10.11 การควบคุมคุณภาพ (Quality Control)
10.12 พรอมใช (Batteries Included)

เรามาดูวาในไลบรารีมาตรฐานของไพธอน มีฟงกชันหรือเมธอดในมอดูล อะไรใหใชบาง

51 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

10.1 งานของระบบปฏิบัติการ (Operating System Interface)

os
มอดูล os มีเมธอดใหใชเยอะมาก
>>> import os
>>> os.system('time 0:02')
0

>>> os.getcwd() # Return the current working directory


'C:\Python24'

>>> os.chdir('/server/accesslogs')

ตองสั่งวา import os เทานั้น หามใชวา from os import * อันนี้เปนภาคบังคับ ไมงั้นชื่อฟงกชันจะตีกันเละ


กับบิลดอินฟงกชัน
ถางงหรือหลงลืม ใหใชบิลดอินฟงกชันสองตัวคือ dir() และ help() ดูวาในมอดูลมีอะไรบาง
>>> import os
>>> dir(os)
<returns a list of all module functions>

>>> help(os)
<returns an extensive manual page created from the module's docstrings>

shutil
สําหรับงานจิปาถะเล็ก ๆ นอย ๆ อาจใชตัวนี้แทน ดูจะอานเขาใจงายกวา
>>> import shutil
>>> shutil.copyfile('data.db', 'archive.db')
>>> shutil.move('/build/executables', 'installdir')

10.2 ไฟลไวลดคารด (File Wildcards)

glob
ใชกับไวลดคารดคลองตัวกวา
>>> import glob
>>> glob.glob('*.py')
['primes.py', 'random.py', 'quote.py']

10.3 อารกิวเมนต (Command Line Arguments)

sys
ใชเยอะมาก เชน สมมุติไฟล demo.py มีโคดแบบนี้
#!/usr/bin/env python
import sys
print sys.argv

พอสั่งงานจากบรรทัดคําสั่งวา "python demo.py one two three" จะไดผลลัพธแบบนี้


$ python demo.py one two three
['demo.py', 'one', 'two', 'three']

52 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

10.4 นําเขา สงออก และเปลี่ยนทิศ (Error Output Redirection and Program Termination)

sys
ใช sys เหมือนเดิม เพราะในนี้มีฟงกชันของ stdin stdout และ stderr เราจะผันการแสดงผลลัพธไปทาง
ไหน ก็ใชจากมอดูลนี้
>>> sys.stderr.write('Warning, log file not found starting a new one
')
Warning, log file not found starting a new one

เวลาตองการหยุดการทํางานของโปรแกรม ใช sys.exit() เปนปกติ

10.5 จับคูสตริง (String Pattern Matching)

re
ใชงาน เรกูลารเอกเพรสชั่น (regular expression) ไดสะดวกและเปนธรรมชาติ
>>> import re

>>> re.findall(rf[a-z]*', 'which foot or hand fell fastest')


['foot', 'fell', 'fastest']

>>> re.sub(r'[a-z]+) ', r'', 'cat in the the hat')


'cat in the hat'

ใชเมธอดของสตริงที่มีอยูแลว
ถางานไมซับซอน ใชอันนี้เร็วกวา อานงายตรวจงาย
>>> 'tea for too'.replace('too', 'two')
'tea for two'

10.6 คณิตศาสตร (Mathematics)

math
มอดูลนี้ ใชงานทศนิยมลอยและไลบรารีมาตรฐานทางคณิตศาตรไดคลองตัวกวา
>>> import math
>>> math.cos(math.pi / 4.0)
0.70710678118654757

>>> math.log(1024, 2)
10.0

random
งานการสุมแยกออกมาจาก math
>>> import random
>>> random.choice(['apple', 'pear', 'banana'])
'apple'

>>> random.sample(xrange(100), 10) # sampling without replacement


[30, 83, 16, 4, 8, 81, 41, 50, 18, 33]

>>> random.random() # random float


0.17970987693706186

53 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> random.randrange(6) # random integer chosen from range(6)


4

10.7 อินเทอรเน็ต (Internet Access)

urllib2
ใชงานดานลูกขาย (client) เชนเปดดู url เปนตน
>>> import urllib2
>>> for line in urllib2.urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl'):
... if 'EST' in line or 'EDT' in line: # look for Eastern Time
... print line

<BR>Nov. 25, 09:43:32 PM EST

smtplib
ใชจัดการเมล
>>> import smtplib
>>> server = smtplib.SMTP('localhost')
>>> server.sendmail('soothsayer@example.org', 'jcaesar@example.org',
"""To: jcaesar@example.org
From: soothsayer@example.org

Beware the Ides of March.


""")

>>> server.quit()

10.8 วันที่และเวลา (Dates and Times)

datetime
มอดูลนี้มีฟงกชันทางวันที่และเวลาเยอะ รวมทั้งยังรองรับเรื่องโซนเวลาดวย
# dates are easily constructed and formatted
>>> from datetime import date
>>> now = date.today()
>>> now
datetime.date(2003, 12, 2)

>>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")


'12-02-03. 02 Dec 2003 is a Tuesday on the 02 day of December.'

# dates support calendar arithmetic


>>> birthday = date(1964, 7, 31)
>>> age = now - birthday
>>> age.days
14368

10.9 การบีบอัดขอมูล (Data Compression)

มีเพียบ zlib gzip bz2 zipfile tarfile


ลองดู zlib เปนตัวอยาง

54 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> import zlib


>>> s = 'witch which has which witches wrist watch'
>>> len(s)
41

>>> t = zlib.compress(s)
>>> len(t)
37

>>> zlib.decompress(t)
'witch which has which witches wrist watch'

>>> zlib.crc32(s)
226805979

10.10 จับประสิทธิภาพ (Performance Measurement)

timeit
ใชจับเวลา ตัวอยางเปนการจับเวลาเทียบกันระหวางการสลับคาแบบโบราณ คือเอาตัวแปรชั่วคราวมาใช
สําหรับสลับ กับการสลับแบบใชทูเปลที่ไพธอนถนัด วาอันไหนเร็วกวา
>>> from timeit import Timer
>>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
0.57535828626024577

>>> Timer('a,b = b,a', 'a=1; b=2').timeit()


0.54962537085770791

สนุก ๆ นะครับ อันนี้เครื่องผม Intel Core2 Duo E6300 เริ่มเกาแลวละ ไพธอนรุน 2.4.4 [GCC 4.2.3 20071123
(prerelease) (Debian 4.2.2-4)]
>>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
0.21430277824401855

>>> Timer('a,b = b,a', 'a=1; b=2').timeit()


0.14265108108520508

จะเห็นวาใชสลับแบบทูเปลเร็วกวาเยอะ โดยเฉพาะซีพียูรุนใหม ๆ (และไพธอนรุนเกือบใหม)

อันนี้เปนแบบเลน ๆ ถาเอาจริงจังก็ยังมีมอดูล profile ใชดูเวลาโคดเยอะ ๆ

10.11 การควบคุมคุณภาพ (Quality Control)

มาถึงนี่ไดก็เริ่มเกาแลวครับ อันนี้ใชกับงานใหญ ๆ ที่เราจะจัดการดูแลโคดเราใหมีมาตรฐานที่ดี เผื่อวาใครจะเอาไป


ใช หรือจะทํางานรวมกับใคร ก็จะเขากันไดแบบไรปญหา

doctest
ใชตรวจสอบมอดูล โดยเราจะตองเขียนผลการทดสอบที่เราหวังวามันจะเปนแบบนั้นไวใน docstring เวลารัน
เขาก็จะทดสอบรันตามนั้น ถาผลตรงกันก็ใชได แตถาผลไมตรง ก็จะรายงานออกมา
def average(values):
"""Computes the arithmetic mean of a list of numbers.

>>> print average([20, 30, 70])


40.0
"""
return sum(values, 0.0) / len(values)

55 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

import doctest
doctest.testmod() # automatically validate the embedded tests

unittest
ใชงานยากกวา แตดูเปนระบบใชไดจริงกวา
import unittest

class TestStatisticalFunctions(unittest.TestCase):

def test_average(self):
self.assertEqual(average([20, 30, 70]), 40.0)
self.assertEqual(round(average([1, 5, 7]), 1), 4.3)
self.assertRaises(ZeroDivisionError, average, [])
self.assertRaises(TypeError, average, 20, 30, 70)

unittest.main() # Calling from the command line invokes all tests

10.12 พรอมใช (Batteries Included)

ไพธอนพยายามทําใหตัวเองพรอมใชงานมากที่สุด จึงพยายามจัดแพกเกจและมอดูลมาใหครบถวน เชน

มอดูล xmlrpclib และ SimpleXMLRPCServer ที่ทํางานกับ XML-RPC ไดทั้งฝงลูกขายและแมขาย โดยที่เรา


ไมตองเขาไปจัดการกับโคด XML เอง
แพกเกจ email ไมเพียงแครับสงอีเมล แตมีฟงกชันของการเขารหัสและปรุงสวนหัวของเมลใหดวย
แพกเกจ xml.dom และ xml.sax ใชในการทํางานกับภาษา XML อยางมีประสิทธิภาพ ในทํานองเดียวกันมอดูล
csv ก็รองรับการทํางานกับไฟล csv รวม ๆ กันแลวก็ทําใหลดความยุงยากในการสงผานขอมูลระหวางงานได
เปนอยางดี
งานที่ตองใชงานหลายภาษาในหลายประเทศ ก็มีมอดูลหรือแพกเกจอยาง gettext locale และ codecs

บางทีการ "พรอมใช" ก็อาจสูมอดูลจากขางนอกไมไดเหมือนกัน แตโดยรวม ๆ แลวถือวาเจงพอตัว

11. เหลือบดูเครื่องมือเซียน (Brief Tour of the


Standard Library - Part II)
11.1 การแสดงผล (Output Formatting)
11.2 เทมเพลต (Templating)
11.3 ขอมูลไบนารี (Working with Binary Data Record Layouts)
11.4 เธรด (Multi-threading)
11.5 ปูม (Logging)
11.6 กําจัดจุดออน (Weak References)
11.7 ใชลิสตใหสะดวก (Tools for Working with Lists)
11.8 เลขทศนิยมลอย (Decimal Floating Point Arithmetic)

11.1 การแสดงผล (Output Formatting)

repr
มอดูล repr มีไวสําหรับปรับฟงกชัน repr() ใหเปนแบบที่เราตองการ

56 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> import repr


>>> repr.repr(set('supercalifragilisticexpialidocious'))
"set(['a', 'c', 'd', 'e', 'f', 'g', ...])"

pprint
ชวยจัดรูปแบบใหเหมาะกับงานที่ไปออกเครื่องพิมพ
>>> import pprint
>>> t = [[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta',
... 'yellow'], 'blue']]]
...

>>> pprint.pprint(t, width=30)


[[[['black', 'cyan'],
'white',
['green', 'red']],
[['magenta', 'yellow'],
'blue']]]

textwrap
เปนอีกตัวนึง แตเหมาะในการจัดการงานขอความ
>>> import textwrap
>>> doc = """The wrap() method is just like fill() except that it returns
... a list of strings instead of one big string with newlines to separate
... the wrapped lines."""
...

>>> print textwrap.fill(doc, width=40)


The wrap() method is just like fill()
except that it returns a list of strings
instead of one big string with newlines
to separate the wrapped lines.

locale
จัดรูปแบบออกมาใหเหมาะกับภาษาของประเทศที่เรากําหนด
>>> import locale
>>> locale.setlocale(locale.LC_ALL, 'English_United States.1252')
'English_United States.1252'

>>> conv = locale.localeconv() # get a mapping of conventions


>>> x = 1234567.8
>>> locale.format("%d", x, grouping=True)
'1,234,567'

>>> locale.format("%s%.*f", (conv['currency_symbol'],


... conv['frac_digits'], x), grouping=True)
'$1,234,567.80'

11.2 เทมเพลต (Templating)

string
เปนเรื่องการจัดการขอความใหเหมาะสม โดยถาเราเขียนโคดใหเรียกใชเทมเพลตแลว เวลาแจกจายโปรแกรม
ไปแลว ผูใชสามารถปรับแตงขอความใหเหมาะสมกับงานของเขาไดงายกวาตองมาแฮกโคดเอง
เวลาใชเอาเครื่องหมาย "$" ใสขางหนาตัวแปร แลวแทนคาตอนรัน (แตถาตองการใช $ ก็แคใสเปน "$$")
>>> from string import Template
>>> t = Template('${village}folk send $$10 to $cause.')
>>> t.substitute(village='Nottingham', cause='the ditch fund')
'Nottinghamfolk send $10 to the ditch fund.'

57 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ถาใสคาตัวแปรในดิกชันนารีผิด เมธอด substitute จะยกขอผิดพลาด KeyError ขึ้นแสดง ในงานบาง


ประเภทผูใชอาจใสคาไมครบ ก็มีเมธอด safe_substitute เพื่อปองกันการแสดงขอความผิดพลาดได
>>> t = Template('Return the $item to $owner.')
>>> d = dict(item='unladen swallow')
>>> t.substitute(d)
Traceback (most recent call last):
. . .
KeyError: 'owner'

>>> t.safe_substitute(d)
'Return the unladen swallow to $owner.'

เรายังสามารถสรางคลาสลูกใหกับ Template ในการแปลงสัญลักษณระบุตัวแปรได ตามตัวอยางเปลี่ยนจาก


สัญลักษณ $ เปน %
>>> import time, os.path
>>> photofiles = ['img_1074.jpg', 'img_1076.jpg', 'img_1077.jpg']
>>> class BatchRename(Template):
... delimiter = '%'
>>> fmt = raw_input('Enter rename style (%d-date %n-seqnum %f-format): ')
Enter rename style (%d-date %n-seqnum %f-format): Ashley_%n%f

>>> t = BatchRename(fmt)
>>> date = time.strftime('%d%b%y')

>>> for i, filename in enumerate(photofiles):


... base, ext = os.path.splitext(filename)
... newname = t.substitute(d=date, n=i, f=ext)
... print '%s --> %s' % (filename, newname)

img_1074.jpg --> Ashley_0.jpg


img_1076.jpg --> Ashley_1.jpg
img_1077.jpg --> Ashley_2.jpg

เทมเพลตแบบนี้เหมาะกับงานรายงาน หรืองาน XML และ HTML

11.3 ขอมูลไบนารี (Working with Binary Data Record Layouts)

struct
มอดูลนี้ใชฟงกชัน pack() และ unpack() ในการทํางานกับขอมูลไบนารีที่มีความยาวไมคงที่ ตัวอยางจะเปน
การวนรอบทํางานกับขอมูลสวนหัวของไฟล ZIP (ใชสัญลักษณ "H" และ "L" แทนขอมูลไบนารีแบบไมนับ
เครื่องหมายขนาด 2 และ 4 ไบต ตามลําดับ)
import struct

data = open('myfile.zip', 'rb').read()


start = 0
for i in range(3): # show the first 3 file headers
start += 14
fields = struct.unpack('LLLHH', data[start:start+16])
crc32, comp_size, uncomp_size, filenamesize, extra_size = fields

start += 16
filename = data[start:start+filenamesize]
start += filenamesize
extra = data[start:start+extra_size]
print filename, hex(crc32), comp_size, uncomp_size

start += extra_size + comp_size # skip to the next header

58 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

11.4 เธรด (Multi-threading)

เธรด เปนการกระจายงานแลวทําขนานกันไป โปรแกรมยุคใหมจําเปนตองมีอยางยิ่ง งานที่เหมาะกับเธรด เชน เธรด


นึงรอผูใชปอนขอมูล อีกเธรดนึงก็ทํางานอื่นขนานกันไป พอผูใชปอนเสร็จก็ประมวลผลเสร็จพอดี เปนตน

threading
ตัวอยางเปนการใชมอดูลนี้บีบอัดขอมูล โดยทําเปนงานเบื้องหลัง
import threading, zipfile

class AsyncZip(threading.Thread):
def __init__(self, infile, outfile):
threading.Thread.__init__(self)
self.infile = infile
self.outfile = outfile
def run(self):
f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
f.write(self.infile)
f.close()
print 'Finished background zip of: ', self.infile

background = AsyncZip('mydata.txt', 'myarchive.zip')


background.start()
print 'The main program continues to run in foreground.'

background.join() # Wait for the background task to finish


print 'Main program waited until background was done.'

จุดที่ยากของงานเธรด คือถาตองมีการแลกเปลี่ยนขอมูลกับโปรแกรมอื่น ๆ มอดูล threading นี้ เตรียมคลาสห


รือฟงกชันในการนี้ไวแลว เชน locks, events, condition variables, and semaphores

Queue
จากขอกังวลเรื่องการแลกเปลี่ยนขอมูลกับงานภายนอกนี่เอง จึงเกิดมอดูลนี้ขึ้นมา ทําใหเราไมตองมาเขียน
เรื่องจังหวะการรอของเธรดเอง

11.5 ปูม (Logging)

งานบันทึกปูม เปนงานนาเบื่อหนอย แตถางานเราใหญขึ้นก็ตองทํา

logging
มอดูลนี้มีเครื่องมือใหพรอมสําหรับการบันทึกปูม งายสุดคือการสงไปใหระบบแสดงคือ sys.stderr
import logging
logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found', 'server.conf')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')

ใหผลแบบนี้
WARNING:root:Warning:config file server.conf not found
ERROR:root:Error occurred
CRITICAL:root:Critical error -- shutting down

คาปริยายของการสงผลงานปูมคือ การแจงขอผิดพลาดของระบบ (standard error) แตเราสามารถดักใหสงไป

59 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ยังจุดอื่นได เชน อีเมล ดาตาแกรม ซอคเก็ต หรือแมกระทั่งแมขายเว็บ และยังอาจเลือกจายไปยังจุดตาง ๆ


แบงตามระดับความสําคัญของขอมูลคือ DEBUG, INFO, WARNING, ERROR, และ CRITICAL

นอกจากนี้ยังสามารถแยกทําเปนไฟลปรับตั้งสําหรับงานปูมโดยเฉพาะ โดยไมตองเขาไปยุงกับโคดหลักเลย

11.6 กําจัดจุดออน (Weak References)

ปกติไพธอนจัดการหนวยความจําไดดีอยูแลว แตมีบางงานที่ยกเวน

weakref
งานตามรอยโคด (tracking) เราตองสรางตัวแปรอางอิงของเราขึ้นมา หลายครั้งตัวที่เราสรางกลับกลายเปน
ปญหาเสียเอง เวลาเราลบตัวแปรไมหมดมันจะไปรบกวนหนวยความจําที่เราตองการดู มอดูล weakref ทํามา
เพื่อการนี้โดยเฉพาะ
>>> import weakref, gc
>>> class A:
... def __init__(self, value):
... self.value = value
... def __repr__(self):
... return str(self.value)
...
>>> a = A(10) # create a reference
>>> d = weakref.WeakValueDictionary()
>>> d['primary'] = a # does not create a reference
>>> d['primary'] # fetch the object if it is still alive
10

>>> del a # remove the one reference


>>> gc.collect() # run garbage collection right away
0

>>> d['primary'] # entry was automatically removed


Traceback (most recent call last):
File "<pyshell#108>", line 1, in -toplevel-
d['primary'] # entry was automatically removed
File "C:/PY24/lib/weakref.py", line 46, in __getitem__
o = self.data[key]()
KeyError: 'primary'

11.7 ใชลิสตใหสะดวก (Tools for Working with Lists)

เพราะลิสตใชงาย เลยมีคนเขียนมอดูลเติมความสามารถใหใชไดหลายหลายยิ่งขึ้น

array
ใชแปลงลิสตเปนแอเรยออปเจคตที่เหมาะกับงานตัวเลข ตัวอยางเปนลิสตของตัวเลขที่เปนจํานวนเต็ม (ปกติ
ใชหนวยความจํา 16 ไบต) ถูกแปลงเปนแอเรยของขอมูลไบนารีไมคิดเครื่องหมายขนาด 2 ไบต (ใชสัญลักษณ
"H" แปลงแลว ใชงานงายมาก

>>> from array import array


>>> a = array('H', [4000, 10, 700, 22222])
>>> sum(a)
26932

>>> a[1:3]
array('H', [10, 700])

60 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

collections
มอดูลนี้มีออปเจคต deque() ที่ใชงานเหมือนลิสต ถาเพิ่มหรือลดสมาชิกขางหนาหรือตอทายแลวจะเร็วกวา
ลิสต แตถาแทรกหรือคนขอมูลจะชากวา เราเลยเอามาใชในงานคิว
>>> from collections import deque
>>> d = deque(["task1", "task2", "task3"])
>>> d.append("task4")
>>> print "Handling", d.popleft()
Handling task1

กับงานคนแบบ Breadth-first search


unsearched = deque([starting_node])
def breadth_first_search(unsearched):
node = unsearched.popleft()
for m in gen_moves(node):
if is_goal(m):
return m
unsearched.append(m)

bisect
ใชเรียงลิสต
>>> import bisect
>>> scores = [(100, 'perl'), (200, 'tcl'), (400, 'lua'), (500, 'python')]
>>> bisect.insort(scores, (300, 'ruby'))
>>> scores
[(100, 'perl'), (200, 'tcl'), (300, 'ruby'), (400, 'lua'), (500, 'python')]

heapq
ใชจัดการฮีป จะเร็วกวาใชลิสตจริง ๆ เพราะงานฮีปเราสนใจแคคาตัวที่นอยที่สุดเทานั้น ไมตองจัดเรียงใหมทั้ง
ลิสต
>>> from heapq import heapify, heappop, heappush
>>> data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
>>> heapify(data) # rearrange the list into heap order
>>> heappush(data, -5) # add a new entry
>>> [heappop(data) for i in range(3)] # fetch the three smallest entries
[-5, 0, 1]

11.8 เลขทศนิยมลอย (Decimal Floating Point Arithmetic)

decimal
มอดูลนี้มีชนิดขอมูลชื่อ Decimal สําหรับเลขทศนิยมลอยที่เหมาะกับงานบัญชี และงานที่ตองการความถูกตอง
ของทศนิยมสูง
ตัวอยางเปนการคํานวณภาษีอัตรารอยละ 5 ของคาโทรศัพท 70 สตางค เทียบกันระหวางใช Decimal กับใช
ทศนิยมลอยของระบบ
>>> from decimal import *
>>> Decimal('0.70') * Decimal('1.05')
Decimal("0.7350")

>>> .70 * 1.05


0.73499999999999999

ฟงกชัน Decimal จะคํานวนเหมือนเราคํานวนดวยมือ จากตัวอยางคือการคูณเลขทศนิยม 2 ตําแหนงเขาดวย


กัน ดังนั้นผลลัพธจะมีทศนิยม 4 ตําแหนง ซึ่งมีความถูกตองกวาการใชทศนิยมลอยของระบบ

ดูตัวอยางขอผิดพลาดจากการหาเศษผลหาร

61 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

>>> Decimal('1.00') % Decimal('.10')


Decimal("0.00")

>>> 1.00 % 0.10


0.09999999999999995

และความผิดพลาดจากการจัดการตัวเลขในลิสต
>>> sum([Decimal('0.1')]*10) == Decimal('1.0')
True

>>> sum([0.1]*10) == 1.0


False

นอกจากนี้ ยังสามารถกําหนดความละเอียดของทศนิยมไดเทาที่เราตองการ
>>> getcontext().prec = 36
>>> Decimal(1) / Decimal(7)
Decimal("0.142857142857142857142857142857142857")

12. เรียนตอที่ไหน (What Now?)


เอกสารหลักของไพธอนเอง
บรรณสารของไพธอน (Python Library Reference) เวลาดูรายละเอียดฟงกชัน มอดูล และอื่น ๆ
ตองมาดูที่นี่สถานเดียว
ติดตั้งมอดูล อธิบายวิธีติดตั้งมอดูลจากภายนอกดวยตนเอง
ภาษาสารของไพธอน (Language Reference) อธิบายโครงสรางทางภาษา
ขอมูลจากแหงอื่น
http://www.python.org/ เว็บหลักของไพธอน
http://docs.python.org/ ทางเขาเอกสารหลักของไพธอน
http://cheeseshop.python.org รายชื่อแพกเกจใหม ๆ รายวัน ถาเราอยากจะแจกจายแพกเกจของเรา ก็
ตองมาที่นี่
http://aspn.activestate.com/ASPN/Python/Cookbook/ ตําราปรุงไพธอนออนไลนของ ActiveState มี
ขายเปนหนังสือดวย ชื่อ Python Cookbook (O'Reilly & Associates, ISBN 0-596-00797-3.)
และอื่น ๆ
news:comp.lang.python กลุมขาวไพธอน
python-list@python.org รายชื่อจดหมายเวียน
คําถามพบบอย FAQ หรืออาจดูที่ไดเรคทอรี่ Misc/ ของแหลงที่เราดาวนโหลดไพธอน
http://mail.python.org/pipermail ปูมประวัติจดหมายเวียน

13. ปจฉิมลิขิต
ยังเรียบเรียงไมหมด ยังขาดอีก 4 เรื่อง ที่ยังไง ๆ ก็ไมแปลเด็ดขาด เพราะทานที่อานจนมาถึงตรงนี้ไดก็คงเกงภาษา
อังกฤษกวาผมแลว :D
คือ

A. Interactive Input Editing and History Substitution


B. Floating Point Arithmetic: Issues and Limitations
C. History and License
D. Glossary

หวังวาคงจะมีทานผูใจบุญมาปรับปรุงใหบทความนี้สมบูรณขึ้น และไดอํานวยประโยชนสุขแกผูอานทุกทาน เทอญ.

62 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ตัวอยางการใชไพธอนทําหองแสดงภาพผานเว็บ
ทําหองแสดงภาพผานเว็บดวยไพธอนอยางงาย
ความตองการคือ

โยนภาพใสไดเรกทอรี่ (ดวยมือ คือใชเชลลคัดลอกเอาเอง) แลวใหโปรแกรมจัดการลดขนาดและสรางภาพ


เล็กสําหรับแสดง
เก็บไฟลภาพเหมือนระบบไฟลปกติ เพื่อไมใหระบบไฟลซับซอนเกินไป
ไมแตะตองไฟลภาพตนฉบับ และตองสามารถเขาถึงได
หมุนภาพอัตโนมัติ และใหมีหนา admin สําหรับหมุนภาพในภายหลัง ในกรณีที่เปนภาพที่ไมมีขอมูล Exif

ใชมอดูล wsgi, Image และเรียกใชโปรแกรมภายนอกคือ imagemagick

เริ่มดวยติดตั้งแพกเกจ และเปดใชมอดูล wsgi


# aptitude install apache2 libapache2-mod-wsgi python-imaging imagemagick
# a2enmod wsgi

สมมุติวารากของ apache2 อยูที่ /var/www


เราจะให url ของหองแสดงภาพเปน http://www.example.com/pythongal
และเนื่องจากเราทําแบบงาย จึงตัดเรื่องบัญชีผูใชออก ลักไกใหหนาของ admin เปน http://www.example.com
/.admin-pythongal ซึ่งกําหนดในโปรแกรม

ติดตั้งโปรแกรมโดยแปลงตัวเปน www-data กอน


# su www-data

ไปที่ไดเรคทอรี่ของ apache2 ดาวนโหลดโปรแกรม ติดตั้งและปรับขออนุญาตใหเรียบรอย


$ cd
$ wget http://www.thaitux.info/files/py/pythongal-511002.tar.gz
$ tar xfz pythongal-511002.tar.gz
$ chmod -R 755 pythongal

เสร็จแลว
ถาเราเอาอะไรใสเขาในไดเรกทอรี่ /var/www/pythongal เขาจะจัดการลดขนาดภาพและสรางภาพเล็กใหเอง โดย
เก็บไฟลเหมือนระบบไฟลปกติ

ปรับแตงหนาตาของ html จากไฟล template.html


และปรับ css ที่ไฟล style.css
ปรับคุณสมบัติของโปรแกรมจากตัวแปรที่หัวไฟล

ลองทดสอบหนาตัวอยางไดที่ www.thaitux.info/pythongal
และหนา admin สําหรับหมุนภาพที่ www.thaitux.info/.admin-pythongal

python: crop ไฟล pdf


มีงานที่จะตองทําไฟลเปน pdf เพื่อสงโรงพิมพ งานนี้ทําจาก Word ในวินโดวส พิมพลงไฟลโดยใชไดรเวอรเครื่อง
พิมพ Image Setter แลวจึงแปลงเปน pdf ดวยลินุกซ ดวยคําสั่ง ps2pdf12 ซึ่งเลือกรุน 1.2 เพราะตองการความเขากัน
ได

แตเนื่องจากขนาดกระดาษของงานเปนขนาด A5 จึงตองเลือกพิมพเปน A4 แทน

63 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

ปญหาคือตัวโปรแกรม ps2pdf ซึ่งไปเรียกใช ghostscript (gs) อีกทีนึง ไมสามารถ crop ขนาดจาก A4 เปน A5 ได
(จริง ๆ แลวอาจทําได แตคนคําสั่งไมพบ และโรงพิมพตองการงานขนาด A5 แบบมีขอบขาวเวนไวดานละ 3 มม. ซึ่ง
คงจะใชคําสั่ง gs ยาก)

คนไปคนมา พบมอดูลไพธอนที่จะทํางานนี้ได คือมอดูล pyPdf

เริ่มเลยแลวกัน

ติดตั้งมอดูล pyPdf
$ sudo aptitude install python-pypdf

เขียนสคริปต ตั้งชื่อวา croppdf.py


$ vi croppdf.py

#!/usr/bin/env python
#prerequisites: aptitude install python-pypdf

import sys
import pyPdf

def usage(progname):
print """
usage: %s "lowerLeft-x lowerLeft-y upperRight-x upperRight-y" infile.pdf outfile.pdf
""" % progname
sys.exit(1)

try:
argl = [ int(i) for i in sys.argv[1].split(" ") if i ]
infile = sys.argv[2]
outfile = sys.argv[3]
inpdf = pyPdf.PdfFileReader(file(infile,"rb"))
outpdf = pyPdf.PdfFileWriter()
for i in range(inpdf.numPages):
page = inpdf.getPage(i)
page.mediaBox.upperRight = tuple(argl[2:])
page.mediaBox.lowerLeft = tuple(argl[:2])
outpdf.addPage(page)

outstream = file(outfile, "wb")


outpdf.write(outstream)
outstream.close()

except:
usage(sys.argv[0])

$ chmod 755 croppdf.py

(พอดีเปนงานดวน เลยเขียนแบบดวนจริง ๆ)

ขั้นตอนการแปลงคือ

1. แปลงจาก ps เปน pdf ดวยคําสั่ง ps2pdf12


$ ps2pdf12 INFILE.ps TEMPFILE.pdf

2. crop เปนขนาด A5 แบบมีขอบขาวขางละ 3 มม. (ประมาณ 9 px)


$ ./croppdf.py "75 238 523 850" TEMPFILE.pdf OUTFILE.pdf

ตัวเลข 4 ตัวคือคาเปนปอยต (pt) ของ x-มุมลางซาย y-มุมลางซาย และ x-มุมบนขวา y-มุมบนขวา ตามลําดับ หาได
โดยการ

64 of 65 10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน) http://debianclub.org/book/export/html/166

แปลงจาก มม. โดยคูณดวย 2.8378


หรือแปลงจากนิ้ว โดยคูณดวย 72

สามารถดูขนาดเอกสารเปนปอยตไดดวยคําสั่ง pdfinfo FILENAME.pdf

แถมอีกตัวอยางนึง
ทําขอบขาวรอบ pdf ขนาด A4 (595x842 ปอยต) โดยเวนระยะ 5 มม. (ประมาณ 14 ปอยต) โดยรอบ
$ ./croppdf.py "-14 -14 609 856" TEMPFILE.pdf OUTFILE.pdf

เสร็จแลวครับ

65 of 65 10/17/2009 12:14 AM

You might also like