Appendix C Lumpy 附录 C Lumpy
本页译自 Think Python 2e(Allen B. Downey)· Appendix C Lumpy。代码块保留英文原文不翻译;正文段段对照,中文块可用右下角按钮隐藏。
Throughout the book, I have used diagrams to represent the state of running programs.
In Section 2.2, we used a state diagram to show the names and values of variables. In Section 3.10 I introduced a stack diagram, which shows one frame for each function call. Each frame shows the parameters and local variables for the function or method. Stack diagrams for recursive functions appear in Section 5.9 and Section 6.5.
Section 10.2 shows what a list looks like in a state diagram, Section 11.4 shows what a dictionary looks like, and Section 12.6 shows two ways to represent tuples.
Section 15.2 introduces object diagrams, which show the state of an object’s attributes, and their attributes, and so on. Section 15.3 has object diagrams for Rectangles and their embedded Points. Section 16.1 shows the state of a Time object. Section 18.2 has a diagram that includes a class object and an instance, each with their own attributes.
Finally, Section 18.8 introduces class diagrams, which show the classes that make up a program and the relationships between them.
These diagrams are based on the Unified Modeling Language (UML), which is a standardized graphical language used by software engineers to communicate about program design, especially for object-oriented programs.
UML is a rich language with many kinds of diagrams that represent many kinds of relationship between objects and classes. What I presented in this book is a small subset of the language, but it is the subset most commonly used in practice.
The purpose of this appendix is to review the diagrams presented in the previous chapters, and to introduce Lumpy. Lumpy, which stands for “UML in Python,” with some of the letters rearranged, is part of Swampy, which you already installed if you worked on the case study in Chapter 4 or Chapter 19, or if you did Exercise 4,
Lumpy uses Python’s inspect module to examine the state of a running program and generate object diagrams (including stack diagrams) and class diagrams.
inspect 模块来检查运行中程序的状态,并生成对象图(含栈图)和类图。C.1 State diagram C.1 状态图
Figure C.1: State diagram generated by Lumpy.
Here’s an example that uses Lumpy to generate a state diagram.
inspect06
The first line imports the Lumpy class from inspect07 . If you don’t have Swampy installed as a package, make sure the Swampy files are in Python’s search path and use this import statement instead:
inspect09 导入 Lumpy 类。如果你没有把 Swampy 当作包安装,请确保 Swampy 文件在 Python 的搜索路径中,并改用下面这条 import 语句:inspect11
The next lines create a Lumpy object and make a “reference” point, which means that Lumpy records the objects that have been defined so far.
Lumpy 对象并设立一个「引用」点,意思是 Lumpy 会记录到目前为止已定义的对象。Next we define new variables and invoke inspect14 , which draws the objects that have been defined since the reference point, in this case inspect, n and n0.
inspect18 ,它会绘制出自引用点以来定义的对象,此例中就是 inspect、n 和 n0。Figure C.1 shows the result. The graphical style is different from what I showed earlier; for example, each reference is represented by a circle next to the variable name and a line to the value. And long strings are truncated. But the information conveyed by the diagram is the same.
The variable names are in a frame labeled inspect22 , which indicates that these are module-level variables, also known as global.
inspect23 的栈帧中,这表明它们是模块级变量,也就是全局变量。You can download this example from inspect24 . Try adding some additional assignments and see what the diagram looks like.
inspect25 下载这个例子。试着添加一些额外的赋值语句,看看图会变成什么样子。C.2 Stack diagram C.2 栈图
Figure C.2: Stack diagram.
Here’s an example that uses Lumpy to generate a stack diagram. You can download it from inspect26 .
inspect27 下载它。inspect28
Figure C.2 shows the result. Each frame is represented with a box that has the function’s name outside and variables inside. Since this function is recursive, there is one frame for each level of recursion.
Remember that a stack diagram shows the state of the program at a particular point in its execution. To get the diagram you want, sometimes you have to think about where to invoke inspect29 .
inspect30 。In this case I invoke inspect31 after executing the base case of the recursion; that way the stack diagram shows each level of the recursion. You can call inspect32 more than once to get a series of snapshots of the program’s execution.
inspect33 ;这样栈图就能显示出递归的每一层。你可以多次调用 inspect34 ,从而得到程序执行过程的一系列快照。C.3 Object diagrams C.3 对象图
Figure C.3: Object diagram.
This example generates an object diagram showing the lists from Section 10.1. You can download it from inspect35 .
inspect36 下载它。inspect37
Figure C.3 shows the result. Lists are represented by a box that shows the indices mapping to the elements. This representation is slightly misleading, since indices are not actually part of the list, but I think they make the diagram easier to read. The empty list is represented by an empty box.
Figure C.4: Object diagram.
And here’s an example showing the dictionaries from Section 11.4. You can download it from inspect38 .
inspect39 下载它。inspect40
Figure C.4 shows the result. n000 is a dictionary that maps from characters (single-letter strings) to integers; inspect maps from integers to lists of strings.
n000 是一个字典,它从字符(单字母字符串)映射到整数;inspect 从整数映射到字符串列表。Figure C.5: Object diagram.
This example generates an object diagram for Point and Rectangle objects, as in Section 15.6. You can download it from inspect45 .
inspect46 下载它。inspect47
Figure C.5 shows the result. inspect48 make a shallow copy, so n00 and n000 have their own Lumpy and import, but they share the same embedded Point object. This kind of sharing is usually fine with immutable objects, but with mutable types, it is highly error-prone.
inspect53 做的是浅拷贝,因此 n00 和 n000 各有自己的 Lumpy 和 import,但它们共享同一个内嵌的 Point 对象。这种共享对不可变对象通常没问题,但对可变类型来说则极易出错。C.4 Function and class objects C.4 函数与类对象
Figure C.6: Object diagram.
When I use Lumpy to make object diagrams, I usually define the functions and classes before I make the reference point. That way, function and class objects don’t appear in the diagram.
But if you are passing functions and classes as parameters, you might want them to appear. This example shows what that looks like; you can download it from inspect58 .
inspect59 下载它。inspect60
Figure C.6 shows the result. Since we invoke inspect61 inside a function, we get a stack diagram with a frame for the module-level variables and for the invocation of inspect62 .
inspect63 ,我们得到一张栈图,其中既有模块级变量的栈帧,也有 inspect64 调用的栈帧。At the module level, Lumpy and inspect66 refer to class objects (which have type n000); inspect68 refers to a function object.
Lumpy 和 inspect70 指向类对象(其类型为 n000);inspect72 指向一个函数对象。This diagram might clarify two points of common confusion: (1) the difference between the class object, Lumpy, and the instance of Point, n00, and (2) the difference between the function object created when inspect75 is defined, and the frame created with it is called.
Lumpy 与 Point 的实例 n00 之间的区别;(2) 定义 inspect78 时创建的函数对象,与调用它时创建的栈帧之间的区别。C.5 Class Diagrams C.5 类图
Figure C.7: Class diagram.
Figure C.8: Class diagram.
Although I distinguish between state diagrams, stack diagrams and object diagrams, they are mostly the same thing: they show the state of a running program at a point in time.
Class diagrams are different. They show the classes that make up a program and the relationships between them. They are timeless in the sense that they describe the program as a whole, not any particular point in time. For example, if an instance of Class A generally contains a reference to an instance of Class B, we say there is a “HAS-A relationship” between those classes.
Here’s an example that shows a HAS-A relationship. You can download it from inspect79 .
inspect80 下载它。inspect81
Figure C.7 shows the result. Each class is represented with a box that contains the name of the class, any methods the class provides, any class variables, and any instance variables. In this example, inspect82 and Lumpy have instance variables, but no methods or class variables.
inspect84 和 Lumpy 都有实例变量,但没有方法或类变量。The arrow from inspect86 to Lumpy shows that Rectangles contain an embedded Point. In addition, inspect88 and Lumpy both inherit from import, which is represented in the diagram with a triangle-headed arrow.
inspect91 指向 Lumpy 的箭头表明 Rectangle 含有一个内嵌的 Point。此外,inspect93 和 Lumpy 都继承自 import,这在图中用一个三角箭头表示。Here’s a more complex example using my solution to Exercise 6. You can download the code from inspect96 ; you will also need inspect97 .
inspect98 下载代码;你还需要 inspect99 。import100
Figure C.8 shows the result. import101 inherits from n000, which inherits from n000. Both n000 and import105 have Cards.
import106 继承自 n000,而 n000 又继承自 n000。n000 和 import111 都含有 Card。This diagram does not show that n000 also has cards, because in the program there are no instances of Hand. This example demonstrates a limitation of Lumpy; it only knows about the attributes and HAS-A relationships of objects that are instantiated.
n000 也含有 card,因为程序中并没有 n000 的实例。这个例子说明了 Lumpy 的一个局限:它只知道那些被实例化的对象的属性和 HAS-A 关系。