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 4 # and a comment on the same line as code
>>> (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 >>> x 0 >>> y 0 >>> z 0 # Zero x, y and z
รองรับทศนิยมจุดลอยตัว (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 "
", line 1, in ? TypeError: can't convert complex to float; use abs(z) >>> a.real 3.0 >>> a.imag 4.0 >>> abs(a) 5.0 # sqrt(a.real**2 + a.imag**2)
พิเศษสําหรับโหมดโตตอบ คาที่ถูกพิมพออกมาเปนครั้งสุดทาย จะถูกเก็บไวในตัวแปรพิเศษคือ _ เราอาจนําตัวแปรนี้ ไปใชในการคํานวนคาตอ ๆ ไปได
>>> 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 ... -H hostname ... """ Usage: thingy [OPTIONS] -h -H hostname
Display this usage message Hostname to connect to Display this usage message 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 + '>' ''
ชองวางระหวางสตริงจะถูกตีความเปนการเชื่อมสตริง แตใชไดกับคาคงที่ (literal) เทานั้น ตัวแปรหรือฟงกชันไมเกี่ยว
>>> 'str' 'ing' 'string' >>> 'str'.strip() + 'ing' 'string' # <# >> 'str'.strip() 'ing' # ", line 1, in ? 'str'.strip() 'ing' ^ SyntaxError: invalid syntax
สตริงทําตัวเปน แอรเรยของอักขระ ดังนั้นจึงสามารถอางถึงแบบแอรเรยได โดยสามารถอางเปนชวงดวย : (colon) ได ซึ่งไพธอนเรียกชวงของสตริงนี้วาสไลซ (slice)
>>> word[4] 'A' >>> word[0:2] 'He' >>> word[2:4] 'lp'
ถาละเลย ไมใสคาดัชนี ถาเปนดัชนีขางหนา จะถูกตีความเปนศูนย และตัวหลังจะถูกตีความเปนความยาวสตริง
>>> word[:2] 'He' >>> word[2:] 'lpA' # The first two characters # Everything except the first two characters
สตริงในไพธอนไมเหมือนกับภาษาซี ไพธอนไมสามารถเปลี่ยนคาสตริงโดยอางจากดัชนีได
>>> word[0] = 'x' Traceback (most recent call last): File "", line 1, in ? TypeError: object doesn't support item assignment >>> word[:1] = 'Splat' Traceback (most recent call last): File "", 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] 'A' >>> word[-2] 'p' >>> word[-2:] 'pA' >>> word[:-2] 'Hel' # The last character # The last-but-one character # The last two characters # Everything except the last two characters
ยกเวน -0 มีคาเทากับ 0 จึงนับจากซายเปนปกติ
>>> word[-0] 'H' # (since -0 equals 0)
ถาระบุดัชนีเปนชวง ไพธอนจะจัดการคาที่ไมเปนจริงใหทั้งหมด แตถาใชดัชนีตัวเดียว ถาคาดัชนีไมเปนจริง ไพธอน จะแสดงขอผิดพลาด
>>> word[-100:] 'HelpA' >>> word[-10] # error Traceback (most recent call last): File "", 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 UnicodeEscape ดูยุงยากเล็กนอย แตจะมีประโยชนสําหรับการใชงาน 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 "", 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]
กําหนดคาเปนชวงสไลซก็ได
>>> ... >>> [1, # Replace some items: a[0:2] = [1, 12] a 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') >>> p [1, [2, 3, 'xtra'], 4] >>> q [2, 3, 'xtra'] # See section 5.1
จากตัวอยางนี้ ตัวแปร 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 >>> f = fib >>> f(100) 1 1 2 3 5 8 13 21 34 55 89 >>> f
ฟงกชันในไพธอนจะคืนคากลับมาเสมอ ซึ่งปกติจะใชคําสั่ง return VALUE แตในตัวอยางขางตนไมมีการคืนคาดวย คําสั่ง return กรณีนี้ไพธอนจะคืนคาเปนคาพิเศษคือ None
>>> print fib(0) None
จากตัวอยางขางตน สามารถเขียนในรูปฟงกชันที่สงคืนคาดังนี้
>>> ... ... ... ... ... ... ... ... >>> >>> [1, 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, 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() parrot(voltage=5.0, 'dead') parrot(110, voltage=220) parrot(actor='John Cleese') # # # # ผิดเพราะขาดคาที่ไมมีคาปริยาย คือ voltage ผิดเพราะคาปริยายอยูซาย จริง ๆ ตองอยูขวา ผิดเพราะกําหนดคาซอน ผิดเพราะชื่อไมมีชื่อคียเวิรด actor
ตัวอยางการรายงานขอผิดพลาดของการกําหนดคาซอน
>>> def function(a): ... pass ... >>> function(0, a=0) Traceback (most recent call last): File "", 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) [3, 4, 5] >>> args = [3, 6] >>> range(*args) [3, 4, 5] # normal call with separate arguments
# call with arguments unpacked from a list
ตัวอยางนี้เปนดิกชันนารี
>>> 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]
>>> >>> >>> [1, a=[1,2,3] a.append(4) a 2, 3, 4]
extend(L)
ยกลิสต L ทั้งยวง ไปขยายตอทาย a คือ a[len(a):] = L
>>> >>> >>> >>> [1, a=[1,2,3] L=[4,5,6] a.extend(L) a 2, 3, 4, 5, 6]
insert(i, x)
แทรกสมาชิก x ในตําแหนง i a.insert(0, x) ก็คือการไปแทรกขางหนา a.insert(len(a), x) ก็คือการไปตอทาย คือ a.append(x)
>>> >>> >>> [1, a=[1,2,3] a.insert(2,0) a 2, 0, 3]
remove(x)
ลบสมาชิกตัวแรกที่มีคาเทากับ x ถาไมมีเทาจะเกิดขอผิดพลาด
>>> >>> >>> [1, a=[1,2,3] a.remove(2) a 3]
>>> a.remove(2) Traceback (most recent call last): File "", 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()
จัดเรียงสมาชิก
>>> >>> >>> [1, a=[1,5,4,2,3] a.sort() a 2, 3, 4, 5]
reverse()
กลับตําแหนงสมาชิก
>>> >>> >>> [3, a=[1,5,4,2,3] a.reverse() a 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()
>>> >>> >>> >>> [3, stack = [3, 4, 5] stack.append(6) stack.append(7) stack 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
>>> >>> ... >>> [0,
seq = range(8) def add(x, y): return x+y map(add, seq, seq) 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 "", 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 "", 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)] >>> >>> >>> [8, vec1 = [2, 4, 6] vec2 = [4, 3, -9] [x*y for x in vec1 for y in vec2] 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() แตเลือกชวงไดดวย จึงตองระบุดัชนีเสมอ
>>> >>> >>> [1, a = [-1, 1, 66.25, 333, 333, 1234.5] del a[0] a 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', >>> len(empty) 0 >>> len(singleton) 1 >>> singleton ('hello',) # <-- note trailing comma
# ไดคาเปน 1 เพราะเปนทูเปลที่มีสมาชิก 1 ตัว
การละเลยการใสวงเล็บในตอนกําหนดคา ไพธอนเรียกวา การอัดขอมูลเปนอนุกรม (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 set(['r', 'd', 'b']) # letters in a but not in b
>>> a | b # letters in either a or b set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l']) >>> a & b set(['a', 'c']) >>> a ^ b set(['r', 'd', 'b', 'm', 'z', 'l']) # letters in both a and b # letters in a or b but not both
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)]) {2: 4, 4: 16, 6: 36} # use a list comprehension
หรือหากคาคียเปนสตริงลวน อาจกําหนดคาแบบนี้ก็ได (เลียนแบบการสงผานคาไปยังฟงกชัน)
>>> 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
... ... apple banana orange pear
print f
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/ __init__.py Formats/ __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... Effects/ __init__.py echo.py surround.py reverse.py ... Filters/ __init__.py equalizer.py vocoder.py karaoke.py ... Top-level package Initialize the sound package Subpackage for file format conversions
Subpackage for sound effects
Subpackage for filters
เมื่อแพกเกจเราถูกอิมพอรต ไพธอนก็จะคนพาธจาก 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' >>> >>> >>> >>> The >>> ... >>> >>> x = 10 * 3.25 y = 200 * 200 s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...' print s 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 Sjoerd
==> ==>
7678 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 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 คือ จากทายไฟล
>>> >>> >>> >>> '5' f = open('/tmp/workfile', 'r+') f.write('0123456789abcdef') f.seek(5) # Go to the 6th byte in the file f.read(1)
>>> 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 "", 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 "", line 1, in ? while True print 'Hello world' ^ SyntaxError: invalid syntax
ในที่นี้คือตก : หลัง True ขอผิดพลาดแบบนี้ตองตามไปแกในโคดอยางเดียว
8.2 สิ่งผิดปรกติตอนทํางาน (Exceptions) เกิดตอนรัน
>>> 10 * (1/0) Traceback (most recent call last): File "", line 1, in ? ZeroDivisionError: integer division or modulo by zero >>> 4 + spam*3 Traceback (most recent call last): File "", 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 "", 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 ... ('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 "", 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 "", 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 "", 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 "", 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 "", line 1, in ? File "", 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: . . .
--รอแปล-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 >>> 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): . . .
ถาคลาสฐานถูกกําหนดไวที่มอดูลอื่น รูปแบบจะเปน
class DerivedClassName(modname.BaseClassName):
คลาสใหมที่แตกออกมานี้ สามารถสรางเมธอดเพื่อครอบงําเมธอดเดิมไดอยางไมมีขอจํากัด โดยที่ถาสรางขึ้นมาแลว เมื่อมีการเรียกเมธอดจะเรียกไปยังเมธอดใหมแทน โคดภายใตเมธอดเดิมจะไมถูกเรียก แตหากยังตองการเรียกโคด จากเมธอดเดิมอยู เราตองเรียกใชเองในรูปแบบ BaseClassName.methodname(self, arguments)
9.5.1 การสืบทอดหลายทาง (Multiple Inheritance)
รูปแบบโครงสรางคือ
class DerivedClassName(Base1, Base2, Base3): . . .
ลําดับการคนหาแอตทริบิวตในอินสแตนซของ 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 "", 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
แตตองระวังการดักตอนแตกลูกแตกหลานคลาส เพราะถาดักพบคลาสแมกอน เขาจะถือวาดักไดแลว และจะเอาขึ้น เลย ลองดู
>>> ... ... >>> ... ... >>> ... ... >>> ... ... ... ... ... ... ... ... ... B C D 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"
>>> 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 >>> it.next() 'a' >>> it.next() 'b' >>> it.next() 'c' >>> it.next() Traceback (most recent call last): File "", 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
... ... f l o g
print char
ทําใหเขียนโคดไดสั้น แตอาจอานยากนิดนึง แตถาใชคลองแลวจะประหยัดโคดไปไดเยอะ เพราะไมตองมานั่งเขียน พวก self.index และ self.data เอง
9.11 เจนเนอเรเตอรเอกซเพรสชั่น (Generator Expressions) รูปแบบเหมือน ลิสตคอมพรีเฮนชั่น (list comprehension) แตใชวงเล็บธรรมดาแทน เขียนและอานโคดงาย และ ประหยัดหนวยความจํา
>>> sum(i*i for i in range(10)) 285 >>> xvec = [10, 20, 30] >>> yvec = [7, 5, 3] >>> sum(x*y for x,y in zip(xvec, yvec)) 260 # sum of squares
# dot product
>>> 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() 'C:\Python24' # Return the current working directory
>>> os.chdir('/server/accesslogs')
ตองสั่งวา import os เทานั้น หามใชวา from os import * อันนี้เปนภาคบังคับ ไมงั้นชื่อฟงกชันจะตีกันเละ กับบิลดอินฟงกชัน ถางงหรือหลงลืม ใหใชบิลดอินฟงกชันสองตัวคือ dir() และ help() ดูวาในมอดูลมีอะไรบาง
>>> import os >>> dir(os) >>> help(os) 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() 0.17970987693706186 # random float
53 of 65
10/17/2009 12:14 AM
Python Tutorial (เรียนรูไพธอน)
http://debianclub.org/book/export/html/166
>>> random.randrange(6) 4
# random integer chosen from range(6)
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
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() unittest
# automatically validate the embedded tests
ใชงานยากกวา แตดูเปนระบบใชไดจริงกวา
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 ทํามา เพื่อการนี้โดยเฉพาะ
>>> >>> ... ... ... ... ... >>> >>> >>> >>> 10 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 # remove the one reference # run garbage collection right away
>>> del a >>> gc.collect() 0
>>> d['primary'] # entry was automatically removed Traceback (most recent call last): File "", line 1, in -topleveld['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