Học Python/Chương V

Tủ sách mở Wikibooks
Buớc tưới chuyển hướng Bước tới tìm kiếm

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ự .