Trở về Mục lục cuốn sách
Xuyên suốt cuốn sách, tôi đã dùng những biểu đồ nhằm thể hiện trạng thái của các chương trình đang chạy.
Ở Mục 2.2, chúng ta dùng một biểu đồ trạng thái để cho thấy tên và giá trị của các biến. Trong Mục 3.10 tôi có giới thiệu một biểu đồ ngăn xếp, trong đó thể hiện mỗi khung cho một lần gọi hàm. Từng khung đều thể hiện các tham số và biến địa phương của hàm hoặc phương thức. Những biểu đồ ngăn xếp cho hàm đệ quy có ở các Mục 5.9 và 6.5.
Mục 10.2 cho thấy dáng vẻ của một danh sách trong biểu đồ trạng thái, Mục 11.4 cho thấy từ điển, và Mục 12.6 thể hiện hai cách biểu diễn một bộ.
Mục 15.2 giới thiệu biểu đồ đối tượng, trong đó cho thấy trạng thái của các thuộc tính của đối tượng, và thuộc tính của những thuộc tính đó, và cứ thư vậy. Mục 15.3 có biểu đồ đối tượng cho Rectangle và các Point đi kèm trong đó. Mục 16.1 cho thấy trạng thái của một đối tượng Time. Mục 18.2 có một biểu đồ gồm một đối tượng lớp và một thực thể, từng cái lại có thuộc tính riêng của mình.
Sau cùng, Mục 18.8 giới thiệu biểu đồ lớp, trong đó cho thấy những lớp hợp thành chương trình, cùng những mối quan hệ giữa chúng.
Những biểu đồ này đều dựa theo Ngôn ngữ mô hình hóa thống nhất (Unified Modeling Language, UML), vốn là một ngôn ngữ đồ thị được chuẩn hóa, dành cho các kĩ sư phần mềm, phục vụ việc trao đổi thông tin về thiết kế chương trình, đặc biệt là những chương trình hướng đối tượng.
UML là một ngôn ngữ phong phú với nhiều loại biểu đồ thể hiện nhiều loại quan hệ giữa đối tượng và các lớp. Những biểu đồ trình bày trong sách này chỉ là một phần nhỏ của ngôn ngữ trên, nhưng chính là phần ngôn ngữ hay được dùng nhất trên thực tế.
Mục đich của phụ lục này là điểm lại những biểu đồ đã trình bày từ các chương trước, đồng thời giới thiệu Lumpy. Lumpy là chữ viết tắt của “UML in Python,” sau khi đảo lại chữ cái; công cụ này là một phần của Swampy, mà bạn đã cài đặt khi thực hiện nghiên cứu cụ thể trong Chương 4 hoặc Chương 19, hay nếu bạn đã làm Bài tập 4,
Lumpy sử dụng module inspect của Python để kiểm tra trạng thái của một chương trình đang chạy và phát sinh ra biểu đồ đối tượng (bao gồm cả biểu đồ ngăn xếp) và biểu đồ lớp.
Biểu đồ trạng thái
Hình 1: Biểu đồ trạng thái được Lumpy tạo ra.
Sau đây là một ví dụ có dùng Lumpy để tạo nên một biểu đồ trạng thái.
from swampy.Lumpy import Lumpy lumpy = Lumpy() lumpy.make_reference() message = 'And now for something completely different' n = 17 pi = 3.1415926535897932 lumpy.object_diagram()
Dòng thứ nhất để nhập lớp Lumpy từ swampy.Lumpy. Nếu bạn không cài Swampy theo hình thức một gói phần mềm, thì hãy đảm bảo rằng các file Swampy đều nằm trong đường dẫn tìm kiếm của Python và dùng câu lệnh import sau đây thay cho lệnh trong đoạn mã trên:
from Lumpy import Lumpy
Các dòng tiếp theo tạo nên một đối tượng Lumpy và lập một điểm “tham chiếu”; tại đó Lumpy ghi lại những đối tượng mà đã được định nghĩa cho đến điểm này.
Sau đó ta định nghĩa các biến mới rồi kích hoạt object_diagram
; lệnh này vẽ nên những đối tượng đã được định nghĩa kể từ điểm tham chiếu, mà trong trường hợp này gồm có message, n và pi.
Hình 1 cho thấy kết quả. Kiểu dáng biểu đồ này khác với cái mà tôi đã trình bày trước đây. Chẳng hạn, từng tham chiếu được biểu diễn bằng một vòng tròn viết cạnh tên biến và một đoạn thẳng chỉ đến giá trị. Đồng thời những chuỗi kí tự dài đều được lược bớt. Song những thông tin mà biểu đồ truyền đạt thì vẫn như vậy.
Tên các biến được viết trong một khung có nhãn hiệu <module>
, vốn để chỉ rằng đây là các biến ở cấp độ module, còn gọi là biến toàn cục.
Bạn có thể tải ví dụ này về từ http://thinkpython.com/code/lumpy_demo1.py. Hãy thử thêm vào một số lệnh gán rồi xem biểu đồ trông như thế nào.
Biểu đồ ngăn xếp
Hình 2: Biểu đồ ngăn xếp
Sau đây là một ví dụ dùng Lumpy để phát sinh một biểu đồ ngăn xếp. Bạn có thể tải về từ địa chỉ http://thinkpython.com/code/lumpy_demo2.py.
from swampy.Lumpy import Lumpy def countdown(n): if n <= 0: print 'Blastoff!' lumpy.object_diagram() else: print n countdown(n-1) lumpy = Lumpy() lumpy.make_reference() countdown(3)
Hình 2 biểu diễn kết quả. Từng khung được biểu diễn bằng một hộp với tên hàm viết ở ngoài và tên biến ở trong. Vì hàm này có tính đệ quy, nên mỗi tầng đệ quy chỉ có một khung.
Hãy nhớ rằng biểu đồ ngăn xếp cho thấy trạng thái của chương trình ở một điểm cụ thể trong quá trình thực thi. Để thu được biểu đồ mong muốn, đôi khi bạn phải suy nghĩ xem nên kích hoạt object_diagram
ở đâu.
Trong trường hợp này tôi kích hoạt object_diagram
sau khi thực thi trường hợp cơ sở của phép đệ quy. Bằng cách này, biểu đồ ngăn xếp sẽ cho thấy từng tầng một của phép đệ quy. Bạn có thể gọi object_diagram
nhiều lần để nhận được một loạt các hình ảnh trạng thái về quá trình thực thi của chương trình.
Biểu đồ đối tượng
Hình 3: Biểu đồ đối tượng
Ví dụ này sẽ phát sinh ra một biểu đồ đối tượng cho thấy những danh sách ở Mục 10.1. Bạn có thể tải mã lệnh về từ http://thinkpython.com/code/lumpy_demo3.py.
from swampy.Lumpy import Lumpy lumpy = Lumpy() lumpy.make_reference() cheeses = ['Cheddar', 'Edam', 'Gouda'] numbers = [17, 123] empty = [] lumpy.object_diagram()
Hình 3 cho thấy kết quả. Các danh sách được biểu thị bởi một hộp cho thấy rõ ràng các chỉ số được ánh xạ đến các phần tử. Cách biểu diễn này hơi làm người xem lạc hướng, vì thực ra các chỉ số không phải thuộc về danh sách; song dù sao tôi nghĩ rằng cách này dễ đọc biểu đồ hơn. Danh sách rỗng được biểu diễn bằng một hộp trống không.
Hình 4: Biểu đồ đối tượng
Và sau đây là một ví dụ cho thấy các từ điển trong Mục 11.4. Bạn có thể tải về từ địa chỉ http://thinkpython.com/code/lumpy_demo4.py.
from swampy.Lumpy import Lumpy lumpy = Lumpy() lumpy.make_reference() hist = histogram('parrot') inverse = invert_dict(hist) lumpy.object_diagram()
Hình 4 cho thấy kết quả. hist là một từ điển để ánh xạ từ những kí tự (chuỗi chỉ có 1 chữ cái) đến những số nguyên; inverse ánh xạ từ các số nguyên đến danh sách các chuỗi.
Hình 5: Biểu đồ đối tượng
Ví dụ này phát sinh ra một biểu đồ đối tượng cho các đối tượng Point và Rectangle, như ở Mục 15.6. Bạn cũng có thể tải nó về từ http://thinkpython.com/code/lumpy_demo5.py.
import copy from swampy.Lumpy import Lumpy lumpy = Lumpy() lumpy.make_reference() box = Rectangle() box.width = 100.0 box.height = 200.0 box.corner = Point() box.corner.x = 0.0 box.corner.y = 0.0 box2 = copy.copy(box) lumpy.object_diagram()
Hình 5 cho thấy kết quả. copy.copy thực hiện “sao chép nông”, vì vậy cả box lẫn box2 đều có width và height riêng, nhưng chúng cùng chia sẻ mối đối tượng Point đi kèm. Hình thức chia sẻ như vậy thường là ổn thỏa đối với các đối tượng bất khả chuyển; song với kiểu khả chuyển, điều này dễ gây nên lỗi.
Các đối tượng hàm và lớp
Hình 6. Biểu đồ đối tượng
Khi dùng Lumpy để lập nên các biểu đồ đối tượng, tôi thường định nghĩa các hàm và lớp trước khi đặt điểm tham chiếu. Bằng cách này, các đối tượng hàm và lớp không xuất hiện trên biểu đồ.
Nhưng nếu bạn truyền các hàm và lớp làm tham số, thì có thể bạn sẽ muốn chúng xuất hiện. Ví dụ sau đây sẽ cho thấy rằng khi chúng xuất hiện thì biểu đồ sẽ như thế nào; bạn có thể tải mã lệnh về từ http://thinkpython.com/code/lumpy_demo6.py.
import copy from swampy.Lumpy import Lumpy lumpy = Lumpy() lumpy.make_reference() class Point(object): """Represents a point in 2-D space.""" class Rectangle(object): """Represents a rectangle.""" def instantiate(constructor): """Instantiates a new object.""" obj = constructor() lumpy.object_diagram() return obj point = instantiate(Point)
Hình 6 biểu thị kết quả. Vì ta kích hoạt object_diagram
bên trong một hàm, ta nhận được một biểu đồ ngăn xếp với một khung dành cho các biến ở cấp độ module và dành cho sự kích hoạt instantiate.
Ở cấp độ module, cả Point lẫn Rectangle đều tham chiếu đến các đối tượng lớp (mà có kiểu là type); instantiate tham chiếu đến một đối tượng hàm.
Biểu đồ này có thể làm rõ hai điều dễ gây nhầm lẫn: (1) sự khác biệt giữa đối tượng lớp là Point, với thực thể của Point là obj, và (2) sự khác biệt giữa đối tượng hàm tạo ra khi định nghĩa instantiate, và khung tạo ra khi nó được gọi đến.
Biểu đồ lớp
Hình 7: Biểu đồ lớp
Hình 8: Biểu đồ lớp.
Mặc dù tôi phân biệt giữa biểu đồ trạng thái, biểu đồ ngăn xếp và biểu đồ đối tượng, song chúng đa phần đều giống nhau ở chỗ đều cho thấy trạng thái của một chương trình đang chạy tại một thời điểm.
Biểu đồ lớp thì khác. Chúng cho thấy các lớp hợp thành một chương trình cũng như những mối quan hệ giữa chúng. Chúng không thay đổi theo thời gian vì chúng mô tả chương trình như một tổng thể chứ không phải tại mộ thời điểm cụ thể nào đó. Chẳng hạn, nếu một thực thể của Lớp A nói chung chứa một tham chiếu đến một thực thể của Lớp B, thì ta nói rằng có một “mối quan hệ HAS-A” giữa hai lớp này.
Sau đây là một ví dụ biểu diễn mối quan hệ HAS-A. Bạn có thể tải nó về từ http://thinkpython.com/code/lumpy_demo7.py.
from swampy.Lumpy import Lumpy lumpy = Lumpy() lumpy.make_reference() box = Rectangle() box.width = 100.0 box.height = 200.0 box.corner = Point() box.corner.x = 0.0 box.corner.y = 0.0 lumpy.class_diagram()
Hình 7 biểu diễn kết quả. Từng lớp đều được biểu diễn bằng một hình hộp trong đó chứa tên của lớp, mọi phương thức mà lớp đó cung cấp, mọi biến của lớp, và mọi biến thực thể. Trong ví dụ này, cả Rectangle lẫn Point đều có biến thực thể, nhưng không có phương thức hay biến lớp.
Mũi tên chạy từ Rectangle đến Point cho thấy rằng các đối tượng Rectangle chứa một Point kèm theo. Ngoài ra, cả Rectangle lẫn Point đều thừa kế từ object, vốn được biểu diễn bằng một mũi tên có đầu tam giác trong biểu đồ này.
Sau đây là một ví dụ phức tạp hơn có dùng lời giải của tôi cho Bài tập 6. Bạn có thể tải mã lệnh về từ http://thinkpython.com/code/lumpy_demo8.py; bạn cũng sẽ phải cần đến http://thinkpython.com/code/PokerHand.py.
from swampy.Lumpy import Lumpy from PokerHand import * lumpy = Lumpy() lumpy.make_reference() deck = Deck() hand = PokerHand() deck.move_cards(hand, 7) lumpy.class_diagram()
Hình 8 cho thấy kết quả. PokerHand thừa kế từ Hand, mà bản thân nó lại thừa kế từ Deck. Cả Deck lẫn PokerHand đều có Card.
Biểu đồ này không cho thấy được rằng Hand cũng có các lá bài, vì trong chương trình không có thực thể nào của Hand. Ví dụ này thể hiện một nhược điểm của Lumpy; nó chỉ biết được những thuộc tính và mối quan hệ HAS-A của những đối tượng nào được tạo nên (được “thực thể hóa”).
Pingback: Think Python: Cách tư duy như nhà khoa học máy tính | Blog của Chiến
Rất cám ơn Anh. Hiện tại em đang nghiên cứu về python chạy trên nền Raspberry Pi ,tài liệu này rất quý đối với em .Chúc Anh có nhiều sức khỏe post nhiều bài hay giúp cộng đồng phát triển, có gì mới gửi cho em theo Email : dungbqc@gmail.com .Thân
Bùi Quốc Dũng
TP Bạc Liêu
Cám ơn Dũng. Rất mong bạn viết được những chương trình hữu dụng trên platform này!
anh có file pdf của think python này ko cho em xin v
Hiện giờ mình không có sẵn file PDF cuốn sách này. Bạn ấn nút [Print Friendly] cuối mỗi bài post để download PDF từng chương vậy.
Tạm thời bây giờ có link này bạn http://162.250.127.146/thinkpython_ebook.pdf