Học Python/Chương V
Diving in
[sửa]Đọc doc strings của module , lớp , hàm để lấy overview của chương trình này nó làm việc như thế nào . Thông thường , đừng lo lắng về những vấn đề bạn không hiểu .
Vì dụ : fileinfo.py
Có thể download ví dụ từ đây (http://diveintopython.org/download/diveintopython−examples−5.4.zip)
"""Framework for getting filetype−specific metadata. Instantiate appropriate class with filename. Returned object acts like a dictionary, with key−value pairs for each piece of metadata. import fileinfo info = fileinfo.MP3FileInfo("/music/ap/mahadeva.mp3") print "\\n".join(["%s=%s" % (k, v) for k, v in info.items()]) Or use listDirectory function to get info on all files in a directory. for info in fileinfo.listDirectory("/music/ap/", [".mp3"]):
Framework có thể được mở rộng bởi thêm những lớp cho các file riêng biệt , HTMLFileInfo, MPGFileInfo, return data.replace("\00", "").strip()
class FileInfo(UserDict): "store file metadata" def __init__(self, filename=None): UserDict.__init__(self) self["name"] = filename class MP3FileInfo(FileInfo): "store ID3v1.0 MP3 tags" tagDataMap = {"title" : ( 3, 33, stripnulls), "artist" : ( 33, 63, stripnulls), "album" : ( 63, 93, stripnulls), "year" : ( 93, 97, stripnulls), "comment" : ( 97, 126, stripnulls), "genre" : (127, 128, ord)} def __parse(self, filename): "parse ID3v1.0 tags from MP3 file" self.clear() try: fsock = open(filename, "rb", 0) try: fsock.seek(−128, 2) tagdata = fsock.read(128) finally: fsock.close() if tagdata[:3] == "TAG": for tag, (start, end, parseFunc) in self.tagDataMap.items(): self[tag] = parseFunc(tagdata[start:end]) except IOError: pass def __setitem__(self, key, item): if key == "name" and item: self.__parse(item) FileInfo.__setitem__(self, key, item) def listDirectory(directory, fileExtList): "get list of file info objects for files of particular extensions" fileList = [os.path.normcase(f) for f in os.listdir(directory)] fileList = [os.path.join(directory, f)for f in fileList if os.path.splitext(f)[1] in fileExtList] def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]): "get file info class from filename extension" subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:] return hasattr(module, subclass) and getattr(module, subclass) or FileInfo return [getFileInfoClass(f)(f) for f in fileList]
if __name__ == "__main__":
for info in listDirectory("/music/_singles/", [".mp3"]): print "\n".join(["%s=%s" % (k, v) for k, v in info.items()]) print
- Đây là chương trình output phụ thuộc trên files trên đỉa cứng của bạn . Để lấy luồng xuất có nghĩa ,bạn sẽ cần thay đổi đường dẫn thư mục tới thư mục trên máy của bạn .
album= artist=Ghost in the Machine title=A Time Long Forgotten (Concept genre=31 name=/music/_singles/a_time_long_forgotten_con.mp3 year=1999 comment=http://mp3.com/ghostmachine album=Rave Mix artist=***DJ MARY−JANE*** title=HELLRAISER****Trance from Hell genre=31 name=/music/_singles/hellraiser.mp3 year=2000 comment=http://mp3.com/DJMARYJANE album=Rave Mix artist=***DJ MARY−JANE*** title=KAIRO****THE BEST GOA genre=31 name=/music/_singles/kairo.mp3 year=2000
Importing Modules Using from module import
[sửa]Python có 2 cách để import modules . Cả hải thì hữu dụng , và bạn sẽ biết khi nào dùng cài nào .
Đây là cách cơ bản from module import syntax :
- from module import * trong Python thì giống như sử dung module trong Perl .
- from module import * trong Python thì giống như import module.* trong java .
Ví dụ : import module vs. from module import
>>> import types >>> types.FunctionType <type 'function'> >>> FunctionType Traceback (innermost last): File "<interactive input>", line 1, in ? NameError: There is no variable named 'FunctionType' >>> from types import FunctionTyp >>> FunctionType <type 'function'>
- module types không chứa phương thức , nó chỉ có thuộc tính cho mổi đối tương Python .
- FunctionType của chính nó thì không được định nghĩa trong không gian namespace .
- Cú pháp này imports thuộc tính FunctionType từ lọai module vào trong vùng không gian .
- Bây giờ FunctionType có thể truy cậy trực tiếp , không cần tham chiếu tới types.
Khi nào bạn sử dụng from module import ?
- Nếu bạn truy cập thuộc tính và phương thức thường hơn
- Nếu bạn muốn chọn lọc import vài thuộc tính và phương thức nhưng không phải cái khác , sử dụng from module import.
- Nếu module chứa thuộc tính hay hàm với cùng tên như là một trong module của bạn , bạn cần dùng module tránh xung đột tên .
Defining classes
[sửa]Python thì hòan tòan hướng đối tượng : bạn có thể đinh nghĩa lớp của chính mình , kế thừa từ lớp được xây dựng bởi chính bạn.
Ví dụ : The Simplest Python Class
class Loaf: pass
- Tên của lớp này là Loaf , và nó không kế thừa tứ bất cứ lớp nào khác . Tên lớp thường viết Hoa, nhưng điều này chỉ là một quy ước ,không đòi hỏi.
- Lớp này không định nghĩa bất cứ thuộc tính hay phương thức nào , nhưng về cú pháp , cần thiết để làm một vài điều gì đó trong định nghĩa , vì vậy bạn dùng pass . Đây là một từ dành riêng của python mà có nghĩa là "đi tiếp , không làm gì khác ở đây ". Nó là một phát biểu mà không làm gì cả .
- Nếu như bạn đóan được điều này , nhưng mỗi thứ trong lớp thì phải thụt vào , giống như code trong hàm , if statement, for loop, ...
Ví dụ : Defining the FileInfo Class
from UserDict import UserDict class FileInfo(UserDict):
Trong python , lớp ông bà của lớp thì đơn giản là được liệt kê vào trong ngoặc đơn lập tức sau tên lớp . Vì vậy lớp FileInfo được kế thừa từ lớp UserDict (được import từ module UserDict). UserDict là một lớp mà họat động giống một từ điển .
Initializing and Coding Classes
[sửa]Ví dụ : Initializing the FileInfo Class
class FileInfo(UserDict): "store file metadata" def __init__(self, filename=None):
- Lớp có thể doc strings , giống module và hàm . __init__ thì được gọi lập tức sau một đối tương của lớp được tạo .
Ví dụ : Coding the FileInfo Class
class FileInfo(UserDict): "store file metadata" def __init__(self, filename=None): UserDict.__init__(self) self["name"] = filename
- Vài ngôn ngữ lập trình hướng đối tượng giả giống Powerbuilder có một khái niệm "extending" constructors và các sự kiện khác , Nơi mà phương thức ông bà được gọi một cách tự động trước khi phương thức con cháu được thực thi . Python không phải làm điều này ; Bạn phải luôn luôn gọi rỏ ràng những hàm thích hợp trong lớp tổ tiên .
- Lớp họat động giống như một thư viện .
Knowing When to Use self and __init__
[sửa]Khi định nghĩa lớp phương thức của bạn , bạn cần liệt kê rỏ ràng self như là tham số đầu tiên phương thúc ,bao gồm __init__ . Khi bạn gọi một phương thức của lớp tổ tiên từ trong lớp của bạn ,bạn cần phải bao gồm tham số self . Nhưng khi bạn gọi lớp phương thức của bạn từ bên ngòai , bạn không cần đặc tả bất cứ thứ gì cho tham số self . Python tự động thêm tham chiếu self cho bạn . Nó thì không thật sự mâu thuẩn , nhưng nó có thể xuất hiện mâu thuẩn bởi vì nó tin cậy trên sự khác nhau ( giữa phương thức giới hạn và không giới hạn) . Tất cả lớp trong Python làm việc cùng 1 cách tương tự , vì vậy một khi bạn đã học một , bạn học hết tất cả chúng . Nếu bạn quên mọi điều khác , nhớ một điều , vì vậy tôi hưa nó sẽ dẫn bạn lên.
Chú ý rằng phương thức __init__ không bao giờ trả về giá trị .
Instantiating classes
[sửa]Thí dụ cụ thể lớp trong Python thì thẵng thắng . Để thuyết minh một lớp , đơn giản gọi lớp như là nó thì ở trong hàm , truyền vào tham số cho phương thức __init__ . Giá trị trả về sẽ là đối tượng được tạo mới .
Ví dụ . Creating a FileInfo Instance
>>> import fileinfo >>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") >>> f.__class__ <class fileinfo.FileInfo at 010EC204> >>> f.__doc__ 'store file metadata' >>> f {'name': '/music/_singles/kairo.mp3'}
Bạn đang tạo một đối tượng của lớp FileInfo (được định nghĩa trong module fileinfo ) và quy cho đối tượng được tạo mới tới biến f . Bạn đang truyền một tham số , /music/_singles/kairo.mp3 , sẽ kết thúc như là tham số filename trong phương thứ __init__ của FileInfo . Mỗi lớp instance có một thuộc tính built-in , __class__ ( chú ý rằng sự miêu tả điều này bao gồm địa chỉ vật lý của instance trong máy )
Bạn có thể truy cậy doc string của instance như là với một hàm hay một module . Tất cả instance của class chia sẽ doc string giống nhau .
Garbage Collection
[sửa]Nếu tạo instances mới thì dể dàng , thì hủy chúng dể dàng hơn . Không có nhu cầu giải phóng instance rỏ ràng . Vì vậy chúng được giải phóng một cách tự động khi biến được ấn định thì ngòai phạm vi . are rare in Python.
Ví dụ : Trying to Implement a Memory Leak
>>> def leakmem(): ... f = fileinfo.FileInfo('/music/_singles/kairo.mp3') ... >>> for i in range(100): ... leakmem()
- Mỗi làn hàm leakmen được gọi , bạn đang tao một instance của FileInfo và ấn định nó tới biến f , là một biến đia phương trong hàm . Sau khi hàm kết thúc , f được giải phóng .
- Không vấn đề , bao nhiêu lần bạn gọi hàm leakmen , nó sẽ không bao giờ leak memory, bởi vì mỗi lần , python sẽ hủy lớp FileInfo mới trước khi trả về leakmem . Giới hạn của kỉ thuật "garbage collection " được "reference counting". Python giữ một danh sách tham chiếu tới mỗi một instance được tạo . Trong ví dụ trên , chỉ có một tham chiếu tới "FileInfo instance" , biến f địa phương . Khi hàm kết thúc , biến f the variable f ngoài phạm vi , vì vậy bộ đếm tham chiếu xuống 0 , và Python hủy tự động instance .
Exploring UserDict : A wrapper class
[sửa]Như bạn đã xem , FileInfo là một lớp mà họat động giống một dictionanry . Xa hơn , chúng ta xem lớp UserDict trong Userm module , là tổ tiên của lớp FileInfor . Điều này thì không đặc biệt , lớp được viết trong python và được lưu trử trong một file .py . Giống bất cứ đọan code nào khác . Trong trường hợp đặc biệt , nó lưu trữ trong lib directory trong cài đặt python của bạn .
Ví dụ : Defining the UserDict Class
class UserDict: def __init__(self, dict=None): self.data = {} if dict is not None: self.update(dict)
- Chú ý rằng UserDict là lớp cơ sở , không kế thừa từ bất kì lớp khác .
- Phương thức __init__ này bạn viết đè lên lớp FileInfo . Chú ý rằng danh sách tham số trong lớp tổ tiên này thì khác biệt hơn . Điều đó thì ổn ; mỗi lớp con có thể có tập hợp tham số của chính nó , củng lâu như là nó gọi tổ tiên với tham số đúng . lớp tổ tiên có một cách để khởi tạo giá trị ( bởi truyền một dictionary trong tham số dict)FileInfo không sử dụng .
- Python hổ trợ thuộc tính dử liệu ( gọi "instance variables" trong java và Powerbuilder , và "member variables" trong C++ ) . Thuộc tính dữ liệu là những miếng dữ liệu được giử lại một instance của một lớp . Trong trường hợp này , mỗi một instance của UserDict sẽ có một thuộc tính dữ liệu . Để tham chiếu thuộc tính này từ code bên ngòai lớp . Với quy ước , tất cả thuôc tính dự liệu thì được khởi tạo tới giá trị hợp lí trong phương thức __init__ . Tuy nhiên , điều này thì không nhất thiết đòi hỏi ,kể từ thuộc tính dữ liệu , giống biến địa phương .
- Phương thức update là một sao chép . Nó sao chép tất cả khóa và giá trị từ dictionary tới cái khác. Điều này không rỏ ràng .
Ví dụ : UserDict Normal Methods
def clear(self): self.data.clear() def copy(self): if self.__class__ is UserDict: return UserDict(self.data) import copy return copy.copy(self) def keys(self): return self.data.keys() def items(self): return self.data.items() def values(self): return self.data.values()
- clear một lớp phương thức bình thường , nó thì sẳn sàng để được gọi bởi bất cứ ai, bất cứ lúc nào. chú ý rằng clear , giống tất cả phương thức lớp , có self như nó là tham số đầu tiên ( Nhớ rằng bạn không bao gồm self khi bạn gọi phương thức
Ví dụ : Inheriting Directly from Built−In Datatype dict
class FileInfo(dict): "store file metadata" def __init__(self, filename=None): self["name"] = filename
- Tham chiếu đầu tiên là cái mà bạn không cần import vào module UserDict , kế từ khi dict là một a built−in datatype và luôn sẳn sàng. Tham chiếu thức hai là cái mà bạn đang kế thừa từ dict trực tiếp, thay vì từ UserDict.UserDict.
- Tham số thứ ba là subtle nhưng quan trọng . Bởi vì cách UserDict làm việc bên trong , nó đòi hỏi bạn để gọi bằng tay .
Special class methods
[sửa]Thêm vào lớp phương thức bình thường , có một số phương thức đặc biệt mà lớp Python có thể định nghĩa . Thay vì gọi trực tiếp bởi code ( giống phương thức bình thường ) , phương thức đặc tả được gọi cho bạn bởi python trong trường hợp đặc biệt .
Getting and Setting Items
[sửa]Ví dụ : The __getitem__ Special Method
def __getitem__(self, key): return self.data[key] >>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") >>> f {'name':'/music/_singles/kairo.mp3'} >>> f.__getitem__("name") '/music/_singles/kairo.mp3' >>> f["name"] '/music/_singles/kairo.mp3'
- Phương thức đặc biết __getitem__ trông khá đơn giản . Giống như phương thức clear bình thướng , keys, and values. Bạn có thể gọi phương thức __getitem__ trực tiếp , nhưng trong thực hành bạn sẽ không thật sự làm điều đó ; Cách đúng để sử dụng __getitem__ là Python gọi nó cho bạn .
- Điều này giống cú pháp bạn sẽ dùng để lấy giá trị dictionary , và trong thực tế nó trả về giá trị bạn mong muốn . Không chỉ bạn có thể gọi nó bởi chính bạn , bạn có thẻ lấy Python để gọi nó cho bạn với sử dụng cú pháp đúng .
Ví dụ : The __setitem__ Special Method
def __setitem__(self, key, item): self.data[key] = item >>> f {'name':'/music/_singles/kairo.mp3'} >>> f.__setitem__("genre", 31) >>> f {'name':'/music/_singles/kairo.mp3', 'genre':31} >>> f["genre"] = 32 >>> f {'name':'/music/_singles/kairo.mp3', 'genre':32}
- Giống phương thức __getitem__ , __setitem__ đơn giản gọi một lần tới dictionary thật self.data để nó làm việc . Và giống __getitem__, bạn sẽ muốn gọi nó một cách bình thường trực tiếp giống cái này; Python gọi __setitem__ cho bạn khi bạn dùng cú pháp đúng .
Ví dụ : Overriding __setitem__ in MP3FileInfo
def __setitem__(self, key, item): if key == "name" and item: self.__parse(item) FileInfo.__setitem__(self, key, item)
- Chú ý rằng phương thức __setitem__ thì được định nghĩa một cách chính xác cùng cách như phương thức tổ tiên. Điều này quan trọng , kể từ khi Python gọi phương thức cho bạn , và nó mong chờ nó được định nghĩa với một số number của tham số .
Advanced Special class methods
[sửa]Python có nhiều phương thức đặc biệt như __getitem__ và __setitem__ . Đôi khi chúng để bạn bắt chước và bạn không biết .
Ví dụ này chỉ vài phương thức đặc biệt trong UserDict .
Ví dụ : More Special Methods in UserDict
def __repr__(self): return repr(self.data) def __cmp__(self, dict): if isinstance(dict, UserDict): return cmp(self.data, dict.data) else: return cmp(self.data, dict) def __len__(self): return len(self.data) def __delitem__(self, key): del self.data[key]
- __repr__ là một phương thức đặc biệt mà được gọi khi bạn gọi repr(instance). hàm repr là một hàm built−in trả về một chuổi miêu tả đối tượng . Nó làm việc trên bất cứ đối tượng nào , không chỉ lớp instances. Bạn đã quen thuộc tường tận với rept rồi ,mà bạn không biết . Trong tương tác window , khi bạn gỏ một tên biến và nhấn ENTER , Python sử dụng repr để hiển thị giá trị của biến . Tạo một từ điển d với vài dử liệu và sau đó in repr(d) để thấy chính bạn .
- __cmp__ được gọi khi bạn so sánh lớp instances . Trong phát sinh , bạn có thể so sánh bất cứ đối tượng Python , không chỉ lớp instances , bởi sử dụng == . Có luật để định nghĩa built-in datatypes . Trong instance , dictionaries thì bằng khi chúng có cùng độ khóa và giá trị . Chuỗi thì bằng khi chúng thì cùng độ dài và chứa những kí tự tuần tự giống nhau . Trong lớp instances , bạn có thể định nghĩa phương thức __cmp__ và code so sánh luận lý của chính bạn và sau đó bạn có thể dùng == để so sánh instances của lớp của bạn và Python sẽ gọi phương thức đặc biệt __cmp__ cho bạn .
- __len__ được gọi khi bạn gọi len(instance) , hàm len là một hàm built-in mà trả về độ dài của đối tượng . __len__ làm việc trên bất cứ đối tượng mà có độ dài . len của một chuổi là số kí tự của nó . len của từ điển là số khóa của nó . len của danh sách hay tuple là số phần tử của nó . cho lớp instances , định nghĩa phương thức __len__ và code tính tóan chiều dài của chính bạn , và khi gọi len(instance) thì Python sẽ gọi phương thúc đặc biệt __len__ cho bạn .
- __delitem__ được gọi khi bạn gọi del del instance[key], mà bạn có thể nhớ như là cách để delete phần tử riêng lẻ từ từ điển . Khi bạn dùng del trong lớp instance , Python gọi phương thức __delitem__ cho bạn .
Introducing class Attributes
[sửa]Ví dụ : Introducing Class Attributes
class MP3FileInfo(FileInfo): "store ID3v1.0 MP3 tags" tagDataMap = {"title" : ( 3, 33, stripnulls), "artist" : ( 33, 63, stripnulls), "album" : ( 63, 93, stripnulls), "year" : ( 93, 97, stripnulls), "comment" : ( 97, 126, stripnulls), "genre" : (127, 128, ord)}
>>> import fileinfo >>> fileinfo.MP3FileInfo <class fileinfo.MP3FileInfo at 01257FDC> >>> fileinfo.MP3FileInfo.tagDataMap {'title': (3, 33, <function stripnulls at 0260C8D4>), 'genre': (127, 128, <built−in function ord>), 'artist': (33, 63, <function stripnulls at 0260C8D4>), 'year': (93, 97, <function stripnulls at 0260C8D4>), 'comment': (97, 126, <function stripnulls at 0260C8D4>), 'album': (63, 93, <function stripnulls at 0260C8D4>)} >>> m = fileinfo.MP3FileInfo() >>> m.tagDataMap {'title': (3, 33, <function stripnulls at 0260C8D4>), 'genre': (127, 128, <built−in function ord>), 'artist': (33, 63, <function stripnulls at 0260C8D4>), 'year': (93, 97, <function stripnulls at 0260C8D4>), 'comment': (97, 126, <function stripnulls at 0260C8D4>), 'album': (63, 93, <function stripnulls at 0260C8D4>)}
- MP3FileInfo là lớp của chính nó , không bất cứ instance đặc biệt của lớp .
- tagDataMap là một thuộc tính của lớp : thật vậy , một thuộc tính của lớp . Nó thì sẵn sàng trước khi tạo bất kì instances của lớp .
Ví dụ : Modifying Class Attributes
>>> class counter: ... count = 0 ... def __init__(self): ... self.__class__.count += 1 ... >>> counter <class __main__.counter at 010EAECC> >>> counter.count 0 >>> c = counter() >>> c.count 1 >>> counter.count 1 >>> d = counter() >>> d.count 2 >>> c.count 2 >>> counter.count
- count là một thuộc tính của lớp counter .
- __class__ là một thuộc tính built-in của mỗi lớp instance . Nó là một tham chiếu tới lớp mà self là một instance của ( trong trường hợp này , lớp counter ).
- Vì vậy count là một thuộc tính lớp , nó thì sẵn sàng thông qua tham chiếu trực tiếp tới lớp . Trước khi bạn tạo bất kì instances của lớp .
- Tạo một instance của lớp , gọi phương thức __init__ , cái mà làm tặng thuộc tính count của lớp lên một . Điều này ảnh hưởng lên lớp của chính nó , không chỉ tạo mới instance .
- Tạo một instance thứ hai sẽ tặng thuộc tính count lên lần nửa . Chú ý , thuộc tính lớp được chia sẽ bơi lớp và tất cả instances của lớp .
Private Functions
[sửa]Giống như hầu hết các ngôn ngữ , Python có khái niệm phần tử riêng ( private elements ) :
- Hàm riêng , không thể được gọi từ bên ngòai module của chúng .
- Phương thức lớp riêng , không thể được gọi từ bên ngòai lớp của chúng .
- Thuộc tính riêng , không thể truy cậy từ bên ngòai lớp của chúng ·
Không giống như phần đông ngôn ngữ , không chắc Hàm , phương thức hay thuộc tính của Pythong là private hay public được xác định hòan tòan bởi tên của nó .
Nếu tên của một hàm Python , phương thức lớp hay thuộc tính bắt đầu với(nhưng không kết thúc với ) hai đường gạch dưới , nó là private , mọi thứ khác là public . Python không có khái niệm lớp phương thức protected ( truy cập chỉ trong lớp của chính nó và các lớp con cháu ). Phương thức lớp thì một trong hai private hay public .
Ví dụ : Trying to Call a Private Method
>>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse'
- Nếu bạn cố gắng gọi phương thức riêng , Python sẽ ném một ngọai lệ , nói rằng cái đó là phương thức không tồn tại . Dĩ nhiên là nó có tồn tại , nhưng nó là private , vì vậy nó không thể truy cậo bên ngòai lớp . Không có cái gì trong Python thật sự là private . Bạn có thể truy cập phương thức __parse của lớp MP3FileInfo bởi tên _MP3FileInfo__parse . Công nhận điều này thì thú vị , nhưng hứa không bao giờ làm điều đó trong code thật sự .