Python cho người Việt

Python cho người Việt

Entries in the Category “Bài viết”

In chuỗi unicode ra màn hình

written by Nguyễn Thành Nam, on Dec 16, 2009 9:33:45 PM.

Một ngày nào đấy, có lẽ bạn sẽ rất bực mình khi gặp phải một lỗi tương tự như sau:

c:\tmp>c:\Python26\python.exe helloworld.py
Traceback (most recent call last):
  File "helloworld.py", line 3, in <module>
    print u"Xin chào bạn, Python!"
  File "c:\Python26\lib\encodings\cp437.py", line 12, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode character u'\u1ea1' in position
 10: character maps to <undefined>

c:\tmp>

Mã nguồn của helloworld.py chỉ đơn giản như sau:

# -*- encoding: utf-8 -*-

print u"Xin chào bạn, Python!"

Lỗi này xảy ra vì chúng ta đang in một chuỗi unicode, với các ký tự nằm ngoài bảng mã ASCII (hoặc bảng mã mặc định của màn hình hiện tại).

Khi gặp phải các ký tự nằm ngoài bảng mã này, Python mặc định sẽ nâng một biệt lệ cho biết chính xác đó là ký tự nào, nằm ở vị trí nào. Như ví dụ trên, đó là ký tự có mã unicode là 1EA1 nằm tại vị trí thứ 10 (vị trí bắt đầu là 0) trong chuỗi Xin chào bạn, Python!.

Có một số cách để khắc phục lỗi này, từ việc thay đổi một số biến môi trường, cho đến việc sửa từng dòng lệnh. Tất cả đều nhằm một mục đích là chuyển chuỗi unicode cần in thành chuỗi byte theo bảng mã UTF-8.

  • Thay đổi biến môi trường: Biến môi trường PYTHONIOENCODING được dùng để định nghĩa bảng mã cho các bộ nhập chuẩn, bộ xuất chuẩn, và bộ lỗi chuẩn (stdin, stdout, stderr). Chúng ta có thể định nghĩa biến môi trường này thành utf-8 như sau:
c:\tmp>set PYTHONIOENCODING=utf-8

c:\tmp>c:\Python26\python.exe helloworld.py
Xin chào bạn, Python!

c:\tmp>
  • Thay đổi bộ xuất chuẩn: Bộ xuất chuẩn (sys.stdout) có thể được “gói” trong bảng mã mới thông qua mô-đun codecs.
# -*- encoding: utf-8 -*-
import codecs
import sys

writer_factory = codecs.getwriter("utf-8")
sys.stdout = writer_factory(sys.stdout)

print u"Xin chào bạn, Python!"
c:\tmp>c:\Python26\python helloworld.py
Xin chào bạn, Python!

c:\tmp>
  • Thay đổi từng dòng lệnh print:
# -*- encoding: utf-8 -*-
print u"Xin chào bạn, Python!".encode("utf-8")
c:\tmp>c:\Python26\python helloworld.py
Xin chào bạn, Python!

c:\tmp>

Như chúng ta thấy, cách nào cũng dẫn đến cùng một kết quả mong muốn. Việc áp dụng cách nào sẽ phụ thuộc vào hoàn cảnh của từng chương trình.

Các bản cài đặt (implementation) của Python

written by Võ Đức Phương, on Dec 14, 2009 11:09:00 AM.

Có lẽ với nhiều người, khi nhắc tới Python sẽ chỉ nghĩ đến trình thông dịch được tải về từ trang web chính thức của ngôn ngữ này: www.python.org. Thế nhưng để đến với ngôn ngữ lập trình Python thì không chỉ có “con đường” đó. Là một chuẩn ngôn ngữ lập trình, Python có rất nhiều bản cài đặt khác nhau, với những mục tiêu cũng như những điểm mạnh, yếu khác nhau. Bài viết này sẽ giới thiệu một trong số những bản cài đặt đó, mong rằng nó sẽ hữu ích đối với những người muốn sử dụng Python nhưng vì một lí do nào đó mà không sử dụng được trình thông dịch chuẩn.

1- CPython:
Phiên bản mới nhất: 2.6 và 3.1
Đây chính là trình thông dịch chuẩn của Python, được coi là bản cài đặt tham chiếu cho ngôn ngữ này. Được viết bằng C và thực thi mã thông qua một trình thông dịch mã trung gian (bytecode interpreter). Là bản cài đặt chính thức, CPython có những tính năng mới nhất của Python, tuy nhiên bị hạn chế về một số mặt như tốc độ.

2- Jython:
Phiên bản mới nhất: 2.5
Được viết bằng Java, chương trình Python sử dụng các lớp của Java (Java class) thay vì mô đun của Python. Ngoài hầu hết các mô đun trong thư viện chuẩn Python, Jython có một ưu điểm rất lớn là có thể sử dụng các lớp trong thư viện chuẩn của Java. Mã lệnh của Jython không được biên dịch ra dạng Python bytecode như CPython mà sang dạng Java bytecode tương tự như các chương trình Java, và tất nhiên mã lệnh này phải được thực thi bởi máy ảo Java chứ không phải trình thông dịch như trong trường hợp của CPython.

3- IronPython:
Phiên bản mới nhất: 2.0, 2.6 (alpha)
Cũng tương tự như Jython, nhưng thay vì được biên dịch ra Java bytecode mà thực thi bởi máy ảo Java, mã lệnh một chương trình IronPython được biên dịch sang dạng CIL (Common Intermediate Language) và thực thi bởi máy ảo .NET. Một chương trình viết bằng IronPython có thể sử dụng được các lớp của khung chương trình .Net, do vậy IronPython rất hữu dụng trong việc tích hợp vào một hệ thống viết bằng .Net .

4- Psyco:
Phiên bản mới nhất: 1.6
Là một bản cài đặt đặc biệt sử dụng phương thức biên dịch tức thời (Just in time compiling) nhằm cải thiện tốc độ thực thi của Python. Tùy thuộc vào loại tác vụ mà Psyco có thể tăng tốc từ 1,5 đến hơn 40 lần.

5- PyPy:
Phiên bản mới nhất: 1.1
Tiền thân là Psyco, PyPy được viết trên một tập con các lệnh của Python (restricted Python - RPython) nhằm nâng cao tính đa nền tảng so với Psyco, ngoài ra việc sử dụng chính Python để viết khiến cho việc thay đổi và tối ưu đơn giản hơn. Cũng như người tiền nhiệm, PyPy là một trình biên dịch tức thời cho phép cải thiện tốc độ thực thi so với CPython. Tuy bắt đầu là một dự án mang tính nghiên cứu nhưng vì thấy được những ưu điểm của PyPy, hiện các tác giả của PyPy đang tập trung vào việc tương thích với CPython nhằm đưa PyPy vào sử dụng trong thực tiễn.

6- Unladen Swallow:
Phiên bản mới nhất: 2009Q3 (alpha)
Là một dự án đặc biệt của Google cũng nhằm tăng tốc độ thực thi của Python, nhưng khác với Psyco hay Pypy là dựa trên nền tảng LLVM. Unladen Swallow có mục đích là tăng ít nhất gấp 5 lần tốc độ của CPython. Điều này sẽ rất có lợi cho Google, do hãng này ứng dụng Python rất nhiều.

Ngoài những bản cài đặt ở trên, ngôn ngữ Python còn rất nhiều bản cài đặt khác như Stackless Python hay WPython v.v… tuy nhiên những bản cài đặt này thường có ứng dụng hẹp hơn, do vậy bài viết chỉ xin giới thiệu những bản cài đặt trên, mong rằng nếu như bạn không ưng ý với CPython thì giờ đây bạn có thể đến với Python theo một cách khác!

Đóng băng ứng dụng với cx_Freeze

written by Nguyễn Thành Nam, on Dec 1, 2009 12:54:13 PM.

Python là ngôn ngữ thông dịch nên để chạy các ứng dụng viết bằng Python, chúng ta cần phải cài bộ thông dịch. Thao tác này hơi luộm thuộm đối với người dùng cuối, nên thông thường chúng ta hay tự hỏi liệu có cách nào đó để chuyển ứng dụng của chúng ta thành một tập tin .EXE như các ứng dụng Windows khác không.

Rất may mắn là chúng ta có thể đạt được mục đích này với chương trình cx_Freeze.

Chúng ta sẽ bắt đầu với một ví dụ đơn giản. Hãy tạo một tập tin helloworld.py với nội dung sau:

# helloworld.py
print "Hello World!"

Khi chạy chương trình bé nhỏ này (giả sử nó đang nằm trong thư mục c:\tmp, chúng ta nhận kết quả như sau:

c:\tmp>c:\Python26\python helloworld.py
Hello World!

c:\tmp>

Bây giờ chúng ta sẽ cài đặt cx_Freeze. Đối với ví dụ trên, chúng ta sẽ phải chọn bản cài đặt cho Windows, và cho phiên bản Python 2.6. Sau khi cài đặt cx_Freeze thì tập tin thực thi sẽ được đặt trong c:\python26\scripts\cxfreeze.

Để đóng băng chương trình helloworld.py, chúng ta thực hiện lệnh cxfreeze helloworld.py:

c:\tmp>c:\Python26\scripts\cxfreeze helloworld.py
coyping c:\python26\lib\site-packages\cx_Freeze\bases\Console.exe -> c:\tmp\dist
\helloworld.exe
coyping C:\Windows\system32\python26.dll -> c:\tmp\dist\python26.dll
writing zip file c:\tmp\dist\helloworld.exe

  Name                      File
  ----                      ----
m StringIO
m UserDict
m __builtin__
m __future__
m __main__                  helloworld.py
m _abcoll
m _bisect
m _codecs
m _codecs_cn
m _codecs_hk
m _codecs_iso2022
m _codecs_jp
m _codecs_kr
m _codecs_tw
m _collections
m _functools
m _heapq
m _locale
m _multibytecodec
m _random
m _sre
m _strptime
m _struct
m _subprocess
m _threading_local
m _warnings
m abc
m array
m base64
m bdb
m binascii
m bisect
m bz2                       c:\python26\DLLs\bz2.pyd
m cPickle
m cStringIO
m calendar
m cmd
m codecs
m collections
m copy
m copy_reg
m cx_Freeze__init__         c:\python26\lib\site-packages\cx_Freeze\initscripts\
Console.py
m datetime
m difflib
m dis
m doctest
m dummy_thread
P encodings
m encodings.aliases
m encodings.ascii
m encodings.base64_codec
m encodings.big5
m encodings.big5hkscs
m encodings.bz2_codec
m encodings.charmap
m encodings.cp037
m encodings.cp1006
m encodings.cp1026
m encodings.cp1140
m encodings.cp1250
m encodings.cp1251
m encodings.cp1252
m encodings.cp1253
m encodings.cp1254
m encodings.cp1255
m encodings.cp1256
m encodings.cp1257
m encodings.cp1258
m encodings.cp424
m encodings.cp437
m encodings.cp500
m encodings.cp737
m encodings.cp775
m encodings.cp850
m encodings.cp852
m encodings.cp855
m encodings.cp856
m encodings.cp857
m encodings.cp860
m encodings.cp861
m encodings.cp862
m encodings.cp863
m encodings.cp864
m encodings.cp865
m encodings.cp866
m encodings.cp869
m encodings.cp874
m encodings.cp875
m encodings.cp932
m encodings.cp949
m encodings.cp950
m encodings.euc_jis_2004
m encodings.euc_jisx0213
m encodings.euc_jp
m encodings.euc_kr
m encodings.gb18030
m encodings.gb2312
m encodings.gbk
m encodings.hex_codec
m encodings.hp_roman8
m encodings.hz
m encodings.idna
m encodings.iso2022_jp
m encodings.iso2022_jp_1
m encodings.iso2022_jp_2
m encodings.iso2022_jp_2004
m encodings.iso2022_jp_3
m encodings.iso2022_jp_ext
m encodings.iso2022_kr
m encodings.iso8859_1
m encodings.iso8859_10
m encodings.iso8859_11
m encodings.iso8859_13
m encodings.iso8859_14
m encodings.iso8859_15
m encodings.iso8859_16
m encodings.iso8859_2
m encodings.iso8859_3
m encodings.iso8859_4
m encodings.iso8859_5
m encodings.iso8859_6
m encodings.iso8859_7
m encodings.iso8859_8
m encodings.iso8859_9
m encodings.johab
m encodings.koi8_r
m encodings.koi8_u
m encodings.latin_1
m encodings.mac_arabic
m encodings.mac_centeuro
m encodings.mac_croatian
m encodings.mac_cyrillic
m encodings.mac_farsi
m encodings.mac_greek
m encodings.mac_iceland
m encodings.mac_latin2
m encodings.mac_roman
m encodings.mac_romanian
m encodings.mac_turkish
m encodings.mbcs
m encodings.palmos
m encodings.ptcp154
m encodings.punycode
m encodings.quopri_codec
m encodings.raw_unicode_escape
m encodings.rot_13
m encodings.shift_jis
m encodings.shift_jis_2004
m encodings.shift_jisx0213
m encodings.string_escape
m encodings.tis_620
m encodings.undefined
m encodings.unicode_escape
m encodings.unicode_internal
m encodings.utf_16
m encodings.utf_16_be
m encodings.utf_16_le
m encodings.utf_32
m encodings.utf_32_be
m encodings.utf_32_le
m encodings.utf_7
m encodings.utf_8
m encodings.utf_8_sig
m encodings.uu_codec
m encodings.zlib_codec
m errno
m functools
m gc
m genericpath
m getopt
m gettext
m heapq
m imp
m inspect
m itertools
m keyword
m linecache
m locale
m marshal
m math
m msvcrt
m nt
m ntpath
m opcode
m operator
m optparse
m os
m pdb
m pickle
m posixpath
m pprint
m quopri
m random
m re
m repr
m select                    c:\python26\DLLs\select.pyd
m shlex
m signal
m sre_compile
m sre_constants
m sre_parse
m stat
m string
m stringprep
m strop
m struct
m subprocess
m sys
m tempfile
m textwrap
m thread
m threading
m time
m token
m tokenize
m traceback
m types
m unicodedata               c:\python26\DLLs\unicodedata.pyd
m unittest
m warnings
m zipimport
m zlib

coyping c:\python26\DLLs\bz2.pyd -> c:\tmp\dist\bz2.pyd
coyping c:\python26\DLLs\select.pyd -> c:\tmp\dist\select.pyd
coyping c:\python26\DLLs\unicodedata.pyd -> c:\tmp\dist\unicodedata.pyd

c:\tmp>

Kết quả của lệnh này là helloworld.py được đóng băng trong thư mục dist:

c:\tmp>dir /s dist
 Volume in drive C is OS
 Volume Serial Number is B07D-8588

 Directory of c:\tmp\dist

12/01/2009  12:42 PM    <DIR>          .
12/01/2009  12:42 PM    <DIR>          ..
12/01/2009  12:42 PM            71,168 bz2.pyd
12/01/2009  12:42 PM         1,423,792 helloworld.exe
12/01/2009  12:42 PM         2,149,888 python26.dll
12/01/2009  12:42 PM            11,776 select.pyd
12/01/2009  12:42 PM           585,728 unicodedata.pyd
               5 File(s)      4,242,352 bytes

     Total Files Listed:
               5 File(s)      4,242,352 bytes
               2 Dir(s)  118,923,808,768 bytes free

c:\tmp>

Và bây giờ chúng ta có thể đóng gói, hoặc nén các tập tin trong thư mục này để chuyển đến người dùng cuối. Tập tin thực thi chính của chúng ta chính là helloworld.exe.

c:\tmp>cd dist

c:\tmp\dist>helloworld.exe
Hello World!

c:\tmp\dist>

cx_Freeze còn nhận một số tham số dòng lệnh khác. Bạn có thể tham khảo thêm bằng cách chạy cxfreeze --help.

Ngoài cx_Freeze còn có một số công cụ khác có chức năng tương tụ ví dụ như py2exe.

Giới thiệu Python

written by vithon, on Nov 25, 2009 9:16:00 PM.

Python là một ngôn ngữ lập trình năng động với nhiều tính năng được sử dụng trong một loạt các ứng dụng. Python thường được so sánh với Tcl, Perl, Ruby, Scheme, hoặc Java. Một vài tính năng đặc trưng của nó gồm:

  • cú pháp rất trong sáng, dễ đọc
  • các khả năng tự xét (introspection) mạnh mẽ
  • hướng đối tượng trực giác
  • cách thể hiện tự nhiên mã thủ tục (procedural code)
  • hoàn toàn mô-đun hóa, hỗ trợ các gói theo cấp bậc
  • xử lý lỗi dựa theo ngoại lệ (exception)
  • kiểu dữ liệu động ở mức rất cao
  • các thư viện chuẩn và các mô-đun ngoài bao quát hầu như mọi việc
  • phần mở rộng và mô-đun dễ dàng viết trong C, C++ (hoặc Java cho Jython, hoặc các ngôn ngữ .NET cho IronPython)
  • có thể nhúng trong ứng dụng như một giao diện kịch (scripting interface)

Python mạnh… và nhanh

Những người hâm mộ Python dùng câu “kèm theo pin” để nói đến bộ thư viện chuẩn, trong đó chứa mọi thứ từ việc xử lý không đồng bộ cho tới các tập tin ZIP. Bản thân ngôn ngữ là một nhà máy linh động có thể giải quyết hầu hết mọi vấn đề. Viết máy chủ web cho riêng bạn với ba dòng mã. Viết mã theo hướng dữ liệu với khả năng tự xét mạnh mẽ và khả chuyển và những tính năng như siêu lớp (meta-classes), kiểu vịt (duck typing)trang hoàng (decorators) của Python.

Python giúp bạn viết mã bạn cần, thật nhanh. Và, nhờ vào trình biên dịch đã được tối ưu hóa, và những thư viện hỗ trợ, mã Python chạy nhanh hơn đa số các ứng dụng cần.

Python hòa hợp tốt với các thứ khác

Python có thể kết nối với các đối tượng COM, .NET, và CORBA.

Với những thư viện Java, sử dụng Jython, một cài đặt của Python cho máy ảo Java.

Với .NET, thử IronPython, cài đặt Python mới của Microsoft cho .NET, hoặc Python for .NET.

Python cũng được hỗ trợ bởi Internet Communications Engine (ICE) và nhiều công nghệ kết nối khác.

Nếu bạn nhận ra một vài điều Python không thể làm, hoặc nếu bạn cần sự thực thi nhanh của mã mức thấp, bạn có thể viết mô-đun mở rộng trong C hoặc C++, hoặc gói mã đang có bằng SWIG hoặc Boost.Python. Các mô-đun đã gói được sử dụng y như những mô-đun Python. Người ta gọi là kết nối ngôn ngữ đã giản đơn. Bạn cũng có thể đi đường ngược lại và nhúng Python vào ứng dụng của bạn, đem lại cho người dùng ngôn ngữ mà họ sẽ say mê.

Python chạy khắp nơi

Python có cho mọi hệ điều hành: Windows, Linux/Unix, OS/2, Mac, Amiga, và những hệ khác. Thậm chí có cả những phiên bản chạy trên .NET, máy ảo Java, và điện thoại di động Nokia Series 60. Bạn sẽ rất hài lòng khi biết rằng cùng một mã nguồn sẽ chạy không khác nhau trên mọi cài đặt.

Hệ thống của bạn không có trong danh sách ở đây? Không sao, nó vẫn có thể chạy Python được nếu có một trình biên dịch C trên hệ thống. Hỏi ở news:comp.lang.python - hoặc cứ thử tự biên dịch Python lại.

Python gần gũi… và dễ học

Nhóm tin Python được biết đến như là một trong những nhóm thân thiện nhất. Cộng đồng nhà phát triển và người dùng mở một wiki, chủ trì các hội thảo quốc tế và địa phương, đứng lớp dạy phát triển, và đóng góp vào kho lưu trữ mã trực tuyến.

Python cũng có kho tài liệu đầy đủ, vừa tồn tại trong bản thân ngôn ngữ lẫn trên mạng. Những tài liệu trên mạng nhắm vào cả lập trình viên chuyên nghiệp lẫn người mới. Tất cả đều được thiết kế nhằm giúp bạn sản xuất nhanh hơn. Và sự có mặt của những quyển sách làm quá trình học tập đầy đủ hơn.

Python mở

Cài đặt Python dùng giấy phép nguồn mở nên được sử dụng và phân tối tự do, ngay cả trong việc thương mại. Giấy phép Python được quán xuyến bởi Python Software Foundation.

Hãy xem qua các lĩnh vực ứng dụng mà Python được dùng, hoặc tự dùng thử bản mới nhất.

Duyệt qua các file trong một thư mục

written by vithon, on Jun 20, 2009 10:45:00 AM.

Bạn thường gặp bài toán liệt kê danh sách tất cả các file trong một thư mục cho trước. Dưới đây là một vài các cách khác nhau tùy theo hoàn cảnh.

1. Sử dụng os.listdir

Câu lệnh sau sẽ lấy danh mục các file trong thư mục Test nằm trong thư mục cá nhân của bạn.

>>> import os
>>> path = "~/Test"
>>> os.listdir(os.path.expanduser(path))
['file 3', 'file 1', 'file 2']

os.listdir chỉ cho bạn tên file hoặc thư mục nằm trong đường dẫn đó. Ví dụ:

>>> path = "/mnt/data/pictures/"
>>> os.listdir(path)
['Old', 'Icons', 'baby.jpg']

Để có đường dẫn đầy đủ bạn có thể viết:

>>> FJoin = os.path.join
>>> files = [FJoin(path, f) for f in os.listdir(path)]
>>> files
['/mnt/data/pictures/Old', '/mnt/data/pictures/Icons', '/mnt/data/pictures/baby.jpg']

Để đảm bảo vấn đề tương thích mã khi chuyển qua lại giữa các hệ điều hành bạn luôn luôn nên dùng: os.path.join để nối các đường dẫn, hoặc dùng os.path.sep để cộng các đường dẫn.

không nên:

path = path1 + "\path2"

nên viết:

path = os.path.join(path1, "path2") 

hoặc

path = path1 + os.path.sep + path2 

Trên Windows bạn có thể viết: path = "~/Test" hoặc "~/test" đều được. Linux phân biệt chữ hoa chữ thường nên phải viết chính xác Test. Ký tự phân tách đường dẫn trên linux là "/", trên Windows bạn có thể viết "/" hoặc tôi hay dùng "\\" (thay vì "\").

2. Sử dụng walk

os.listdir chỉ liệt kê các file và thư mục trong một thư mục. Để lấy được cả danh sách đệ quy các file trong thư mục bạn có thể dùng os.walk hoặc thư viện ngoài glob.

Hàm os.walk là một “generator function” và os.walk(path) là một “generator object”. Nghĩa là hàm trạng thái trả về các kết quả kế tiếp nhau theo yêu cầu. Mối một item của walk có ba thành phần: 1. Thư mục hiện tại, 2. Các thư mục con, 3. Các file bên trong. Dưới đây là hàm sử dụng walk để liệt kê tất cả các file và thư mục con trong một thư mục cho trước, kết quả trả về chứa đường dẫn đầy đủ:

import os

FJoin = os.path.join

def GetFiles(path):
    """ Output: file_list là danh sách tất cả các file trong path và trong tất cả các
         thư mục con bên trong nó. dir_list là danh sách tất cả các thư mục con
         của nó. Các output đều chứa đường dẫn đầy đủ."""

    file_list, dir_list = [], []
    for dir, subdirs, files in os.walk(path):
        file_list.extend([FJoin(dir, f) for f in files])
        dir_list.extend([FJoin(dir, d) for d in subdirs])
    return file_list, dir_list

if __name__ == "__main__":
    files, dirs = GetFiles(os.path.expanduser("~/Music"))
    for file in files:
        print file
    for dir in dirs:
        print dir

Hàm này chạy tốt trên Windows, trên Linux nó lấy cả các link. Nói chung các link sẽ không có ý nghĩa trong đa số các trường hợp của bạn. Để loại bỏ các link bạn viêt lại hàm GetFiles như sau:

def GetFiles(path):
    file_list, dir_list = [], []
    for dir, subdirs, files in os.walk(path):
        file_list.extend([FJoin(dir, f) for f in files])
        dir_list.extend([FJoin(dir, d) for d in subdirs])
    file_list = filter(lambda x: not os.path.islink(x), file_list)   
    dir_list = filter(lambda x: not os.path.islink(x), dir_list)   
    return file_list, dir_list

Chú ý rằng một object vừa có thể là file vừa có thể là link. Vì vậy bạn không thể dùng hàm os.path.isfile để lọc các link được:

>>> os.path.islink('/home/hoaiptm/Music/mylink')
True
>>> os.path.isfile('/home/hoaiptm/Music/mylink')
True

Tương tự như vậy với hàm os.path.isdir, nó cũng trả về True nếu object là link liên kết đến một thư mục khác.

3. Sử dụng glob

glob là cách liệt kê file và thư mục theo pattern, các pattern có thể chứa các ký tự đại diện như *, ?, [], nó cho phép bạn tìm kiếm chỉ các file có tên thỏa mãn quy tắc pattern cho trước. Ví dụ:

# liệt kê các file và thư mục trong thư mục path
glob.glob(os.path.join(path, "*"))
# liệt kê các file và thư mục cấp 2 bên trong thư mục path.
# chẳng hạn path chứa thư mục A, B, C thì lệnh trên sẽ
# liệt kê hết các file và thư mục bên trong A, B, C.
glob.glob(os.path.join(path, "*", "*"))
# liệt kê các file và thư mục trong thư mục path bắt đầu với chữ cái a.
glob.glob(os.path.join(path, "[a]*"))
# liệt kê các file và thư mục trong thư mục path
# có tên kết thúc bằng chữ cái p và có đúng 3 ký tự.
glob.glob(os.path.join(path, "??p"))

Chú ý rằng không giống như listdir hoặc walk, glob lấy đường dẫn đầy đủ.

Hàm glob không thực hiện đệ quy. Hàm sau đây sử dụng glob để tìm kiếm đệ quy tất cả các file và thư mục bên trong thư mục cho trước:

import os
from os import path
import glob
 
def dirwalk(dir, bag, wildcards):
    bag.extend(glob.glob(path.join(dir, wildcards)))
    for f in os.listdir(dir):
        fullpath = os.path.join(dir, f)
        if os.path.isdir(fullpath) and not os.path.islink(fullpath):  
            dirwalk(fullpath, bag, wildcards)

files = []

# Lấy tất cả các file và thư mục con trong thư mục path:
dirwalk(path, files,  "*")

# Lấy tất cả các file trong thư mục path (có thể có lẫn thư mục):
dirwalk(path, files, "*.*")

# Lấy tất cả các file python trong thư mục path:
dirwalk(path, files,  "*.py")

# Lấy tất cả các file hoặc thư mục bắt đầu bằng "py" trong thư mục path:
dirwalk(path, files,  "py*.*")

Hàm dirwalk ở triển khai này làm được nhiều việc hơn hàm GetFiles triển khai ở mục 2. Nó tổng quát hơn.

Một cách triển khai khác có thể viết như thế này:

def dirwalk2(dir, bag, wildcards):
    """ bag là một list chứa các file thỏa mãn quy tắc wildcards """

    if glob.glob(path.join(dir, "*")):
        bag.extend(glob.glob(path.join(dir, wildcards)))
        dirwalk2(path.join(dir, "*"), bag, wildcards)

dirwalk2 tuy ngắn gọn nhưng khó hiểu hơn dirwalk. nó hoàn toàn tương tự ngoại trừ việc không kiểm tra một object có thể là link (chỉ có ý nghĩa trên linux).

4. Thư mục có chứa tên file tiếng Việt

Các ví dụ phía trên giả thiết rằng bạn có các tên file và thư mục là tiếng anh thông thường hoặc tiếng việt không dấu. Thư mục có thể chứa các file có tên tiếng việt hoặc tiếng Tàu, tiếng Hàn, … bạn phải truyền đối số unicode cho các hàm listdir, walk, glob… Quy tắc này áp dụng cho tất cả các hàm và thủ tục khác thao tác với file. Quan sát ví dụ sau (trên Windows):

>>> import os
>>> FJoin = os.path.join
>>> FExists = os.path.exists
>>> path = "D:/abc"
>>> os.listdir(path)
['V? mi?n t\xe2y']
>>> [FJoin(path, f) for f in os.listdir(path)]
['D:/abc\\V? mi?n t\xe2y']
>>> [FExists(FJoin(path, f)) for f in os.listdir(path)]
[False]

Thư mục abc có chứa duy nhất file “Về miền tây”, tên file là tiếng Việt có dấu, kết quả kiểm tra sự tồn tại cho thấy file không tồn tại. Nguyên nhân là từ hàm listdir. Nêu đối số của hàm listdir là unicode các tên file lấy về cũng là unicode, nếu là ascii, các tên file trả về cũng là dạng ascii. Vì vậy tên file trả về của listdir trong trường hợp này không đúng và bạn không thể thao tác với file đó được. Để lấy đúng tên file trong trường hợp này bạn phải truyền đối số unicode cho hàm listdir:

>>> path = u"D:/abc"
>>> [FJoin(path, f) for f in os.listdir(path)]
[u'D:/abc\\V\u1ec1 mi\u1ec1n t\xe2y']
>>> [FExists(FJoin(path, f)) for f in os.listdir(path)]
[True]

Tuy nhiên trên Linux, bạn không gặp vấn đề này.

5. Bài tập ví dụ:

Viết chương chình đổi tên tất cả các file trong thư mục “c:\data” và các thư mục con của nó. Đổi tên các file có phần mở rộng là “*.htm” thành “*.docx”.

Dưới đây là một triển khai của bài tập này:

# -*- coding: utf-8 -*-
#!/usr/bin/env python
 
from os import path
import os
import glob
 
def dirwalk(dir, bag, wildcards):
    """ bag là một list chứa các file thỏa mãn quy tắc wildcards """
     
    if glob.glob(path.join(dir, "*")):
        bag.extend(glob.glob(path.join(dir, wildcards)))
        dirwalk(path.join(dir, "*"), bag, wildcards)
 
def rename(oldName, newExt):
    """ Thay thế phần mở rộng cũ thành mở rộng mới. Giả thiết oldName luôn có
     phần mở rộng. """
     
    newName = oldName[0:oldName.rfind(".") + 1] + newExt
    os.rename(oldName, newName)
    return newName
 
def WalkAndRename(dir, oldExt, newExt):
    files = []
    dirwalk(dir, files, u"*" + oldExt)
     
    for f in files:
        print f, "-->", rename(f, newExt)
 
if __name__ == "__main__":
    WalkAndRename('c:\\data', "htm", 'docx')


# The End

(Bài viết được chuyển từ diễn đàn do bạn Phạm Thị Minh Hoài đóng góp)

Xử lý tiếng Việt trong Python

written by vithon, on Jun 14, 2009 2:23:00 AM.

Ví dụ sau minh họa 3 codecs bạn hay gặp nhất trong Python:

>>> s = "sky down no enemy"
>>> print type(s), len(s)
<type 'str'> 17
>>> s = "thiên hạ vô địch"
>>> s
'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'
>>> print type(s), len(s)
<type 'str'> 23
>>> s = unicode("thiên hạ vô địch", 'utf8')
>>> s
u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'
>>> print type(s), len(s)
<type 'unicode'> 16

Nghiên cứu ví dụ trên. len(s) trong hai ví dụ đầu thực sự là số byte cần để lưu trữ s. len(s) trong ví dụ 3 là độ dài ký tự, dung lượng nhớ thực sự để lưu trữ s là 16 * 2 = 32 bytes.

Tại bất cứ một thời điểm nào trong chương trình Python bạn cũng phải xác định được bạn đang thao tác với chuỗi kiểu gì: ascii, unicode, hay utf8… Dùng lệnh sau để biết một chuỗi là unicode hay không:

>>> s
u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'
>>> type(s) == type(u"")
True
>>> ss = "sky down no enemy"
>>> type(ss) == type(u"")
False
>>> s = "thiên hạ vô địch"
>>> type(ss) == type(u"")
False

Cộng một chuỗi không phải unicode với một chuỗi unicode thì chuỗi không phải unicode sẽ được tự động convert sang unicode trước khi cộng:

>>> ss = "sky down no enemy"
>>> ss + u""
u'sky down no enemy'
>>> ss = "thiên hạ vô địch"
>>> ss + u""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)
>>> ss = unicode("thiên hạ vô địch", 'utf8')
>>> ss + u""
u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'

Trong tình huống thứ 2 bạn cố gắng cộng một chuỗi utf8 vào chuỗi unicode. Python tự động chuyển chuỗi utf8 thành chuỗi unicode bằng cách decode với codec=’ascii’, vì vậy gặp lỗi. Tình huống này tương đương với:

ss + u"" = ss.decode('ascii') + u""
>>> ss = "thiên hạ vô địch"
>>> ss.decode('ascii') + u""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)

Hai cách để cộng đúng là:

ss.decode('utf8') + u""

hoặc

unicode(ss, 'utf8') + u""

Các thao tác với chuỗi khác như join, split, find, …cũng tương tự. Nghĩa là nếu có một tham số unicode thì các tham số còn lại được tự động chuyển sang unicode trước khi thực hiện thao tác. Ví dụ:

>>> ss = "sky down no enemy"
>>> ss.split(u" ")
[u'sky', u'down', u'no', u'enemy']
>>> l = ["sky", "down", "no", "enemy"]
>>> unichr(32).join(l)
u'sky down no enemy'

Tương tự thao tác sau sẽ gây lỗi:

>>> s = "thiên hạ vô địch"
>>> s.split(u" ")
Traceback (most recent call last):
  File "<string>", line 1, in <string>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)

Để chuyển một chuỗi tiếng Việt utf sang dạng viết hoa, bạn luôn luôn phải chuyển nó về unicode trước. Nghiên cứu ví dụ sau:

>>> s = "thiên hạ vô địch"
>>> s.upper()
'THI\xc3\xaaN H\xe1\xba\xa1 V\xc3\xb4 \xc4\x91\xe1\xbb\x8bCH'
>>> s
'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'

So sánh với s ta thấy s.upper() không upper case được các ký tự ê, ạ, ô, đ, ị, …

Để upper một chuỗi utf bạn phải chuyển nó về unicode trước, upper xong thì chuyển ngược lại. Hàm Upper sau nhận đầu vào là chuỗi utf, upper nó và trả về utf khác đã được upper, hàm này thích hợp cho mục đích upper tiếng Việt:

# -*- coding: utf-8 -*-

def Upper(s):
   if type(s) == type(u""):
      return s.upper()
   return unicode(s, "utf8").upper().encode("utf8")

if __name__ == "__main__":
   s = "thiên hạ vô địch"
   us = s.decode('utf8')

   print s.upper()
   print Upper(s)
   print Upper(us)

Kết quả:

THIêN Hạ Vô địCH
THIÊN HẠ VÔ ĐỊCH
THIÊN HẠ VÔ ĐỊCH

Đầu vào thích hợp của Upper là ascii, utf8, unicode, các dạng khác có thể sai: latin1, tcvn, … Tương tự như vậy cho hàm lower, và capwords. Xem xét ví sau với capwords:

# -*- coding: utf-8 -*-

import string

s = "thiên hạ vô địch"

print string.capwords(s)
print string.capwords(s.decode('utf8'))

Kết quả:

Thiên Hạ Vô địch
Thiên Hạ Vô Địch

Các tên file hoặc tên thư mục là tiếng Việt có dấu cũng đòi hỏi cách xử lý đặc biệt. Giả sử bạn có thư mục abc với một file duy nhất tên là: “tiếng việt.txt”. Thư mục abc đặt trong thư mục cá nhân của bạn. Nghiên cứu đoạn chương trình sau:

# -*- coding: utf-8 -*-

import os

path = os.path.join(os.path.expanduser("~"), "abc") 
files = [os.path.join(path, basename) for basename in os.listdir(path)]

print map(os.path.exists, files)

Chú ý:

  • Trên Windows path = “C:\Documents and Settings\YourAccountName\abc”
  • Trên Linux path = “/home/YourAccountName/abc”

Vì chỉ có duy nhất một file “tiếng việt.txt” trong thư mục abc nên chúng ta mong đợi kết quả in ra là: [True]. Tuy nhiên chỉ Linux cho câu trả lời này. Windows thì không. Không tin bạn thử xem. Chương trình trên cho dù đã được viết với việc tận dụng thư viện sẵn có os nhằm nâng cao tính khả chuyển nhưng vẫn không khả chuyển, khi tên file là tiếng Việt có dấu.

Giải quyết vấn đề này rất đơn giản bạn chỉ cần thay “abc” thành u”abc” để tất cả các đường dẫn tên file bị ép chuyển sang unicode là OK. Bạn làm như sau:

path = os.path.join(os.path.expanduser("~"), u"abc") 

hoặc

path = os.path.join(os.path.expanduser(u"~"), "abc") 	

Các vấn đề tương tự cũng áp dụng cho hàm glob hoặc walk, Các hàm này cần các đường dẫn là chuỗi unicode để có thể lấy chính xác các file có tên tiếng Việt.

Loại bỏ dấu tiếng Việt. Trong nhiều trường hợp bạn cần loại bỏ dấu của một chuỗi tiếng Việt có dấu. Chẳng hạn chuyển “tiếng việt” –> “tieng viet”. Đây là một cách thức đơn giản giải quyết vấn đề này:

# -*- coding: utf-8 -*-

import string
import re

INTAB = "ạảãàáâậầấẩẫăắằặẳẵóòọõỏôộổỗồốơờớợởỡéèẻẹẽêếềệểễúùụủũưựữửừứíìịỉĩýỳỷỵỹđ"
INTAB = [ch.encode('utf8') for ch in unicode(INTAB, 'utf8')]

OUTTAB = "a"*17 + "o"*17 + "e"*11 + "u"*11 + "i"*5 + "y"*5 + "d"

r = re.compile("|".join(INTAB))
replaces_dict = dict(zip(INTAB, OUTTAB))

def khongdau(utf8_str):
    return r.sub(lambda m: replaces_dict[m.group(0)], utf8_str)

print khongdau("thiên hạ vô địch")
print khongdau("sky down no enemy")
print khongdau("THIÊN HẠ VÔ ĐỊCH")

Kết quả:

thien ha vo dich
sky down no enemy
THIÊN HẠ VÔ ĐỊCH

Test thứ ba cho thấy nó chưa làm việc với chữ hoa. Một chút cải tiến nhỏ để nó làm việc với chữ hoa các bạn có thể thêm vào dễ dàng. Chương trình này chưa được test kỹ về sự chính xác và về performance. Các bạn tự test và công bố kết quả nhé.

Slice với chuỗi tiếng việt dạng utf8 có thể gặp các vấn đề. Thao tác tương tự khi bạn đọc các khối dữ liệu utf8 với kích thước quy định trước từ file utf8. Xem xét các ví dụ sau:

>>> s = 'thiên hạ vô địch'
>>> s
'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'
>>> s[7:17]
'h\xe1\xba\xa1 v\xc3\xb4 \xc4'

chuỗi s[7:17] là chuỗi què. byte cuối cùng của chuỗi này \xc4 mới là một nửa của chữ cái đ (’\xc4\x91’), vì vậy mọi thao tác của bạn trên chuỗi này có tiềm năng gặp lỗi. chẳng hạn:

>>> unicode(s[7:17], 'utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 9: unexpected end of data
>>> unicode(s[7:16], 'utf8')
u'h\u1ea1 v\xf4 '

Unicode áp dụng cho s[7:16] thì vô tư vì nó không bị què.

Việc đọc các file text utf8 cũng gặp tình huống tương tự. Chẳng hạn thao tác sau tiềm năng gặp lỗi:

f = open("file name", "r")
s = f.read(1000)
...
s.close()

Trong tình huống này bạn cố gắng đọc 1000 byte đầu tiên của file, s có thể là chuỗi tiếng Việt bị què như tình huống ở trên.

Đọc ghi file dữ liệu tiếng Việt. File chứa dữ liệu dạng văn bản tiếng Việt thường được ghi dưới dạng unicode hoặc utf8. Đoạn chương trình sau đọc nội dung utf8:

ff = open("anyfile", 'r')
content = ff.read()
ff.close()

Dữ liệu tiếng Việt lưu dưới dạng utf8 thường có BOM_UTF8 (= “\xef\xbb\xbf”) ở đầu file. Bạn phải loại bỏ cái này trước khi có thể làm cái gì đó. Đoạn chương trình sau làm việc này:

import codecs

content = open("anyfile", 'r').read()
if content.startswith(codecs.BOM_UTF8):
    content = content[3:]

Chú ý rằng đoạn chương trình trên không thích hợp cho việc xử lý các file lớn (hơn 200MB). Với các file lớn bạn cần chia nhỏ thành các file nhỏ hơn.

codecs là thư viện chứa rất nhiều BOM.

>>> dir(codecs)
['BOM', 'BOM32_BE', 'BOM32_LE', 'BOM64_BE', 'BOM64_LE', 'BOM_BE', 'BOM_LE', 'BOM_UTF16', 'BOM_UTF16_BE', 'BOM_UTF16_LE', 'BOM_UTF32', 'BOM_UTF32_BE', 'BOM_UTF32_LE', 'BOM_UTF8', ...]

Dùng thư viện codecs bạn có thể nhanh chóng đọc nội dung file mà không mất nhiều công biến đổi encoding. Ví dụ:

# đọc toàn bộ nội dung của file vào content,
# nội dung của file được biết trước như là utf8,
# content sẽ là nội dung unicode.
content = codecs.open('your file name', 'r', 'utf8').read() 
# mặc định đọc toàn bộ nội dung của file vào content
# (dạng mặc định là utf8)
content = codecs.open('your file name', 'r').read()

Ghi dữ liệu tiếng Việt ra file:

ff = open("filename", 'w').write(content)

Ở đây content là chuỗi utf8, nếu bạn đưa vào content là dạng unicode, nó sẽ được tự động chuyển về dạng utf8 trước khi được khi ra file.

(Bài viết được chuyển từ diễn đàn do bạn Phạm Thị Minh Hoài đóng góp)

Cấu trúc đơn giản của một chương trình Python

written by vithon, on May 19, 2009 8:09:00 AM.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Bạn có thể comment bằng tiếng Việt có dấu thoải mái. Hãy dùng tín hiệu ở
# dòng trên để báo cho các Editor biết được đây là file utf8.

""" đây là cách thức được dùng để viết docstring cho một khối """

# Viết thêm cái này cho nó đẹp

"""
#-----------------------------------------------------------------------------
# Name:        Module Name
#
# Purpose:     
#
# Version:     1.1
#
# Author:      
#
# Created:     13/01/2009
# Updated:     30/04/2009
#
# Copyright:   (c) YourCompany
#
# Todo: 
#-----------------------------------------------------------------------------
"""

# Import các thư viện
from xml import sax
import re
import os

# Khai báo và triển khai các lớp

class YourClass:
    """ 
    Đặt các mô tả về lớp ở đây
    """
    
    def __init__(self):
        """ Viết một cái gì đó để giải thích nếu cần thiết """

        # KHÔNG nên viết quá 3 lệnh python trên một dòng, chương trình
        # của bạn sẽ rất khó hiểu.
        self.a, self.b = [], {}

        pass

# KHÔNG nên viết một dòng Python dài hơn 80 ký tự

def Test():
    pass

# Các lập trình viên python thường dùng cách này để test module

if __name__ == "__main__":
    Test()

(Bài viết được chuyển từ diễn đàn do bạn Phạm Thị Minh Hoài đóng góp)

Python và PHP

written by vithon, on Jan 30, 2009 10:01:00 AM.

Bài viết này được tổng hợp từ nhiều nguồn khác nhau (bằng tiếng anh) cộng với kinh nghiệm viết PHP và Python của tôi.

I. Giống nhau

  • Là ngôn ngữ cao cấp, thông dịch và định kiểu động
  • Mã nguồn mở
  • Cộng đồng lập trình viên rộng lớn
  • Dễ học (so với Java, C hay Perl)
  • Dễ mở rộng (so với Java, C++)
  • Rất khả chuyển, chạy được trên nhiều nền tảng mà không phải biên dịch lại (tất nhiên rồi) hay sửa code

II. Khác nhau

1. PHP hơn

  • PHP kế thừa các curly braces để đánh dấu code block của C và dấu $ của Perl
  • Các câu lệnh switch và vòng lặp do/while
  • Toán tử gán, increment và decrement (++--)
  • Toán tử ternary operator/statement (… ? … : …)
    • Trong Python 2.5 có câu lệnh if/else có tác dụng tương tự (X if C else Y)
  • Array rất mạnh ,đùng cho cả list và dict
  • Tham chiếu ($a =& $b tức là khi biến $b thay đổi, thì biến $a cũng thay đổi)
  • Có cả private, protected và public access modifiers cho cả phương thức và thuộc tính
  • Dùng cả abstract và final modifiers cho cả classes và methods
  • Có interfaces
  • $this là đối tượng mặc định, và không cần truyền ngược self để định nghĩa method như Python (với Python, để định nghĩa test là method của một lớp cần định nghĩa: def test(self))

2. Python hơn

  • Ngôn ngữ sử dụng với các mục đích chung (Python có thể dùng để lập trình hầu hết mọi thứ, trong khi PHP chỉ dùng để lập trình web hay console. Tất nhiên có thể làm được việc khác nhưng không đáng kể)
  • Sử dụng indentation (thụt vào đầu dòng, và dòng mới) thay thế cho các dấu curly braces để đánh dấu các code block. Tất nhiên là trông đẹp hơn, nhưng không thân thiện với HTML template cho Web)
  • Có namespaces và modules (PHP 5.3 mới có namespaces)
  • Truyền biến cho hàm hiệu quả hơn PHP nhờ cặp key=value pair, hỗ trợ tốt hơn cho giá trị mặc định của biến truyền vào hàm
  • Cho phép đa thừa kế (không rõ là hay hay là dở nữa)
  • Introspection tốt hơn PHP Reflection
  • Mọi thứ từ biến tới đối tượng đều có kiểu tham chiếu
  • Có Threading
  • Document tốt hơn PHP (nhưng chắc chắn là ít tài liệu trên mạng hơn PHP)
  • Có thể lập trình GUI (Tất nhiên là PHP cũng có thể nhưng không thể bằng)
  • Có thể chạy trên .Net với IronPython hay nền Java với Jython (PHP cũng có thể chạy trên Java thậm chí trong cả các Application Server)
  • Có web server, application server riêng, 100% viết bằng Python.
  • Có lambda và các hàm hỗ trợ để xây dựng hàm (PHP có create_function và closure)
  • Cached byte-code (PHP cũng có nhưng không tốt bằng)
  • Unicode được hỗ trợ tốt hơn
  • Kiểm soát lỗi ngoại lệ tốt hơn
  • Chạy nhanh hơn PHP (nhanh hơn nhưng tốn CPU và RAM)

Hãy học Python cho đời còn lại của bạn nhé!

(Bài viết được chuyển từ diễn đàn do bạn kaka đóng góp)

Grinder - Java load testing framework

written by vithon, on Nov 30, 2007 4:14:00 AM.

Những người làm phần mềm hẳn ai cũng đã biết đến công đoạn rất quan trọng sau khi thành phẩm là “kiểm thử” (test). Một trong những kiểu kiểm thử (test case) cần phải làm là kiểm thử khả năng chịu đựng của hệ thống (load test). Có một công cụ rất mạnh để tự động hóa quá trình này, tiết kiệm thời gian và công sức cho đội kiểm thử đó là Grinder.

Giới thiệu

Tại sao lại là Grinder mà không phải là một công cụ nào khác? Đơn giản là vì Grinder sử dụng Jython - Python chạy trên nền Java - một sự kết hợp tuyệt vời của một ngôn ngữ rất mềm dẻo, mạnh mẽ (Python) với một khung nền có thể chạy trên mọi hệ điều hành (Java).

Grinder bao gồm 3 thành phần:

  • Console: đây là thành phần đóng vai trò điều khiển và tiếp nhận kết quả kiểm thử trả về.
  • Agent process: đây là thành phần nhận lệnh từ Console và điều khiển các worker process.
  • Worker process: đây là các kịch bản kiểm thử do người dùng định nghĩa.

Như vậy, tại máy dùng để theo dõi và hiển thị kết quả kiểm thử, chạy Console. Tại các máy trạm chịu trách nhiệm chạy các kịch bản kiểm thử, chạy Agent process. Các kịch bản kiểm thử sẽ được tự động chuyển về cho các Agent process từ Console khi người dùng ra lệnh chạy.

Chuẩn bị

Để chạy được Grinder cần phải cài đặt các biến môi trường trỏ về đúng thư mục cài đặt Grinder và các thành phần liên quan.

Đối với Windows

  • Tạo tập tin kịch bản gọi mõi khi chạy setGrinderEnv.cmd:
set GRINDERPATH=(full path to grinder install directory)
set GRINDERPROPERTIES=(full path to grinder.properties)\grinder.properties
set CLASSPATH=%GRINDERPATH%\lib\grinder.jar;%CLASSPATH%
set JAVA_HOME=(full path to java install directory)
PATH=%JAVA_HOME%\bin;%PATH%
  • Hoặc gán trực tiếp biến môi trường mỗi khi khởi động bằng cách:
    • Vào System Properties chọn tab Advanced chọn tiếp Environment Variables
    • Chọn New và thêm vào tên biến và đường dẫn như trên.

Đối với Unix

  • Tạo tập tin kịch bản setGrinderEnv.sh:
#!/usr/bin/ksh
GRINDERPATH=(full path to grinder install directory)
GRINDERPROPERTIES=(full path to grinder.properties)/grinder.properties
CLASSPATH=$GRINDERPATH/lib/grinder.jar:$CLASSPATH
JAVA_HOME=(full path to java install directory)
PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH PATH GRINDERPROPERTIES
  • Hoặc gán trực tiếp mỗi khi đăng nhập vào người dùng bằng cách thêm vào trong tập tin /home/username/.bashrc:
export GRINDERPATH=(full path to grinder install directory)
export GRINDERPROPERTIES=(full path to grinder.properties)/grinder.properties
export CLASSPATH=$GRINDERPATH/lib/grinder.jar:$CLASSPATH
export JAVA_HOME=(full path to java install directory)

Cấu hình

Tập tin thuộc tính grinder.properties thường gồm các tham số sau:

Tên tham sốGiải thíchGiá trị mặc định
grinder.processesSố lượng tiến trình worker mà một agent khởi tạo1
grinder.threadsSố lượng tiểu trình mà mỗi tiến trình worker sinh ra1
grinder.runsSố lần chạy kịch bản kiểm thử mà mỗi tiểu trình chạy. 0 nghĩa là chạy lặp đi lặp lại. Tham số này nên để là 0 khi sử dụng Console để điều khiển hoạt động của các agent.1
grinder.processIncrementNếu đặt, agent sẽ tăng số lượng tiếng trình worker lên, số lượng tăng lên xác định bởi grinder.processIncrement (tính theo mili giây).Khởi động tất cả các tiến trình worker cùng lúc.
grinder.processIncrementIntervalSử dụng cùng với tham số grinder.processIncrement, tham số này xác định nhịp tăng tiến trình worker tính theo mili giây.60000 ms
grinder.initialProcessesSử dụng cùng với tham số grinder.processIncrement, tham số này xác định số lượng tiến trình worker khởi động.
grinder.durationThời gian mỗi tiến trình worker chạy. Tham số này có thể xác định cùng với tham số grinder.runs, điều đó có nghĩa là tiến trình worker sẽ chấm dứt nếu hết thời gian chạy hoặc số lượng tiến trình worker đạt tối đa.Không kết thúc
grinder.scriptTên tập tin kịch bản sẽ chạy.grinder.py
grinder.jvmSử dụng máo ảo Java (JVM) khác. Mặc định là Java nên không cần phải khai báo trong đường dẫn PATH.java
grinder.jvm.classpathDùng để xác định đường dẫn đến các lớp sử dụng cho tiến trình worker. Bất cứ đường dẫn nào xác định ở đây sẽ được thêm vào trong đường dẫn classpath sử dụng để khởi động tiến trình Grinder.
grinder.jvm.argumentsCác tham số phụ thêm cho tiến trình worker.
grinder.logDirectoryThư mục lưu tập tin log. Tự động tạo nếu chưa có.Thư mục cục bộ
grinder.numberOfOldLogsSố lượng nhật kí được lưu lại từ lần chạy trước đó.1
grinder.hostIDThay thế chuỗi “host” sử dụng trong tên tập tin nhật kí và trong nhật kí.Tên máy
grinder.consoleHostĐịa chỉ IP hoặc tên máy sử dụng cho kết nối giữa các agent, tiến trình worker và console.Tất cả các giao tiếp mạng của máy.
grinder.consolePortCổng để agent và tiến trình worker sử dụng để kết nối đến console.>6372
grinder.useConsoleNếu đặt là false, agent và tiến trình worker sẽ không sử dụng console.true
grinder.reportToConsole.intervalKhoảng thời gian mỗi tiến trình gửi cập nhật về cho console. Tham số này cũng xác định khoảng thời gian các tập tin dữ liệu được làm mới.500 ms
grinder.initialSleepTimeKhoảng thời gian tối đa mỗi tiểu trình đợi trước khi bắt đầu. Không giống như thời gian ngừng trong kịch bản, tham số này thay đổi ngẫu nhiên giữa 0 và giá trị được đặt. Bị ảnh hưởng bởi tham số grinder.sleepTimeFactor, còn tham số grinder.sleepTimeVariation thì không.0 ms
grinder.sleepTimeFactorThêm một hệ số vào thời gian chờ đã đặt, thông qua một thuộc tính hay trong kịch bản. Đặt giá trị này là 0.1 kịch bản sẽ chạy nhanh hơn mười lần.1
grinder.sleepTimeVariationThông thường, Grinder thay đổi thời gian chờ xác định trong kịch bản tùy theo sự phân chia bình thường. Tham số này xác định một dải phân đoạn mà đa số (chiếm 99.75%) là sẽ rơi vào trong dải này. Ví dụ, nếu thời gian chờ xác định là 1000 và sleepTimeVariation đặt là 0.1, khi đó 99.75% các giá trị thời gian chờ sẽ nằm trong khoảng 900 và 1100 milli giây.0.2
grinder.logProcessStreamsĐặt là false để tắt tính năng ghi nhật kí những thông báo và lỗi hiển thị của tiến trình worker. Dùng tham số này để giảm tải cho các tiểu trình tại máy trạm.true
grinder.reportTimesToConsoleĐặt là false để tắt tính năng phản hồi thông tin thời gian về cho console, các thông tin thống kê khác vẫn được phản hồi.true
grinder.debug.singleprocessNếu đặt là true, agent sẽ phát sinh tiểu trình để chạy kịch bản thay vì tiến trình, sử dụng một bộ nạp lớp đặc biệt để cô lập engine ra. Điều này cho phép engine dễ gỡ rối hơn. Mục đích chính của công cụ này là gỡ rối Grinder engine, tuy nhiên nó cũng có thể dùng cho người dùng chuyên nghiệp vào các mục đích khác.false
grinder.useNanoTimeNết đặt là true, System.nanoTime() sẽ được dùng để đo thời gian thay vì System.currentTimeMills(). Grinder sẽ vẫn phản hồi thời gian tính bằng mili giây. Độ chính xác của những phương pháp này phụ thuộc vào sự hiện thực hóa JVM và hệ điều hành. Tham số true chỉ dùng cho J2SE 5 hoặc lớn hơn.false

Ví dụ

Thông thường, một tập tin cấu hình đơn giản chỉ cần các thông số sau:

grinder.process 1
grinder.threads 1
grinder.runs 1
grinder.script sample.py

Nếu chạy ở chế độ có Console thì cần thêm:

grinder.useConsole true
grinder.consoleHost x.x.x.x (IP hoặc tên máy Console)
grinder.consolePort xxx (cổng quy định bởi Console)

Chạy

Để chạy Grinder có thể đơn giản như sau:

  • Chạy Console:
java net.grinder.Console
  • Chạy Agent:
java net.grinder.Grinder

Hoặc tạo tập tin kịch bản như sau:

Đối với Windows

startAgent.cmd:

call (path to setGrinderEnv.cmd)\setGrinderEnv.cmd
echo %CLASSPATH%
java -cp %CLASSPATH% net.grinder.Grinder %GRINDERPROPERTIES%

startConsole.cmd:

call (path to setGrinderEnv.cmd)\setGrinderEnv.cmd
java -cp %CLASSPATH% net.grinder.Console

Đối với Unix

startAgent.sh:

#!/usr/bin/ksh
. (path to setGrinderEnv.sh)/setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.Grinder $GRINDERPROPERTIES

startConsole.sh:

#!/usr/bin/ksh
. (path to setGrinderEnv.sh)/setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.Console

Công việc tiếp theo chỉ là .... viết kịch bản kiểm thử và chạy.

Tự động hóa

Đến đây hẳn nhiều người, nhất là những lập trình viên không (chưa) biết ngôn ngữ Python sẽ tiếc nuối vì không biết viết mã cho kịch bản kiểm thử như thế nào. Nhưng có một cách để những lập trình viên này vẫn có thể dùng công cụ kiểm thử mạnh mẽ Grinder mà không cần phải biết viết một dòng mã Python nào. Từ phiên bản 3.0 trở lên, Grinder cung cấp một môi trường proxy kiểm soát dữ liệu vào ra để tự động tạo mã kịch bản tương ứng. Điều đó có nghĩa là, kịch bản sẽ tự động được tạo ra theo đúng những thao tác chuột và bàn phím mà những người kiểm thử làm. Và tất nhiên cách này chỉ dùng cho những kiểm thử đối với các ứng dụng web.

Để khởi động Grinder proxy:

Đối với Windows

startProxy.cmd:

call (path to setGrinderEnv.cmd)\setGrinderEnv.cmd
java -cp %CLASSPATH% net.grinder.TCPProxy -console -http > script_name.py

Đối với Unix

startProxy.sh:

#!/usr/bin/ksh
. (path to setGrinderEnv.sh)/setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.TCPProxy -console -http > script_name.py

Sau đó thiết đặt trình duyệt cho trỏ đến proxy là localhost với cổng 8081 và bắt đầu các thao tác kiểm thử.

Tổng kết

Trên đây là những bước cơ bản nhất để có thể làm quen với một công cụ kiểm thử vô cùng mạnh mẽ, đơn giản, dễ dùng và đặc biệt là hoàn toàn miễn phí. Mặc dù chạy trên nền Java nhưng trong quá trình thử nghiệm cũng như sử dụng để kiểm thử hệ thống Online Trading của công ty Chứng khoán VNDirect, Grinder chạy rất nhẹ nhàng, không tốn nhiều RAM như tôi lo ngại, thậm chí tôi sử dụng chính các máy của người dùng làm agent trong khi mọi người vẫn làm việc mà không hề thấy hệ thống bị kéo chậm lại hay có vấn đề gì xảy ra. Việc làm quen với Grinder cũng không mất quá nhiều thời gian, kể cả với một tay ngang như tôi. Grinder thật sự rất ấn tượng. Và điểm nổi trội nhất của Grinder đó là nó cho phép người dùng tự định nghĩa kịch bản kiểm thử bằng một ngôn ngữ mạnh mẽ và mềm dẻo - Python.

(bài viết được bạn Lê Ngọc Hiếu đóng góp)

Python S60 cho người mới bắt đầu

written by vithon, on Aug 18, 2007 7:54:00 PM.

Xin chào các bạn, mình là thành viên mới của forum. Mình thực sự rất yêu thích Python, và cũng mới bắt đầu nghiên cứu về Python trong thời gian gần đây. Đặc biệt mình có nghiên cứu thêm về phát triển ứng dụng Python cho điện thoại Nokia S60. Trong quá trình học mình có ghi chép lại những gì đã học thành tài liệu Python S60 cho người mới bắt đầu. Tài liệu này mình vẫn viết, được cập nhật hàng ngày và có thể xem cũng như download tại http://my.opera.com/noname00/forums/topic.dml?id=198943 (Khuyến cáo nên down bản PDF, vì nếu có thay đổi thì bản PDF sẽ được thay đổi chứ mình không thay đổi trên forum :D)

Mình nghĩ nó thích hợp cho cả những người mới bắt đầu làm quen với Python (như mình) cũng như những người thích lập trình Python cho điện thoại di động.

Mong các bạn cho ý kiến

(chép lại từ diễn đàn, do bạn Trịnh Thành Trung đóng góp)