← 学习库 Think Python (2e) · 中英对照 目录

Chapter 15  Classes and objects 第 15 章 类与对象

本页译自 Think Python 2e(Allen B. Downey)· Chapter 15 Classes and objects。代码块保留英文原文不翻译;正文段段对照,中文块可用右下角按钮隐藏。

Code examples from this chapter are available from http://thinkpython.com/code/Point1.py; solutions to the exercises are available from http://thinkpython.com/code/Point1_soln.py.

本章的代码示例可从 http://thinkpython.com/code/Point1.py 获取;习题解答可从 http://thinkpython.com/code/Point1_soln.py 获取。

15.1 User-defined types 15.1 用户自定义类型

We have used many of Python’s built-in types; now we are going to define a new type. As an example, we will create a type called Point that represents a point in two-dimensional space.

我们已经用过 Python 的许多内置类型;现在要定义一个新类型。举个例子,我们会创建一个名为 Point 的类型来表示二维空间中的一个点。

In mathematical notation, points are often written in parentheses with a comma separating the coordinates. For example, (0,0) represents the origin, and (x,y) represents the point x units to the right and y units up from the origin.

在数学中,点通常用括号表示,坐标之间用逗号分隔。例如,(0,0) 表示原点,(x,y) 表示从原点向右 x 个单位、向上 y 个单位处的点。

There are several ways we might represent points in Python:

在 Python 里,我们可以用几种方式表示点:

Creating a new type is (a little) more complicated than the other options, but it has advantages that will be apparent soon.

创建一个新类型(稍微)比其他方案复杂一点,但它有很快就能看出来的好处。

A user-defined type is also called a class. A class definition looks like this:

用户自定义的类型也叫 。类的定义长这样:
Point0010

This header indicates that the new class is a Point, which is a kind of Point0, which is a built-in type.

这个头部说明新类是一个 Point,它是 Point0 这个内置类型的一种。

The body is a docstring that explains what the class is for. You can define variables and functions inside a class definition, but we will get back to that later.

类体是一个文档字符串,说明这个类是做什么用的。你可以在类定义内部定义变量和函数,不过我们稍后再讲。

Defining a class named Point creates a class object.

定义一个名为 Point 的类会创建一个类对象。
Point0017

Because Point is defined at the top level, its “full name” is Point0019.

因为 Point 定义在最顶层,所以它的「全名」是 Point0021。

The class object is like a factory for creating objects. To create a Point, you call Point as if it were a function.

类对象就像生产对象的工厂。要创建一个 Point,就把它当函数一样调用。
Point0023

The return value is a reference to a Point object, which we assign to Point. Creating a new object is called instantiation, and the object is an instance of the class.

返回值是 Point 对象的一个引用,我们把它赋给 Point。创建新对象叫做 实例化,这个对象就是该类的一个 实例

When you print an instance, Python tells you what class it belongs to and where it is stored in memory (the prefix x0 means that the following number is in hexadecimal).

打印实例时,Python 会告诉你它属于哪个类以及存放在内存的什么位置(前缀 x0 表示后面的数字是十六进制)。

15.2 Attributes 15.2 属性

You can assign values to an instance using dot notation:

你可以用点号语法给实例赋值:
Point0028

This syntax is similar to the syntax for selecting a variable from a module, such as Point00 or Point0030. In this case, though, we are assigning values to named elements of an object. These elements are called attributes.

这个语法和从模块里选取变量的语法相似,比如 Point00 或 Point0032。不过这里我们是给对象的具名元素赋值。这些元素叫做 属性

As a noun, “AT-trib-ute” is pronounced with emphasis on the first syllable, as opposed to “a-TRIB-ute,” which is a verb.

作为名词,attribute(属性)重音在第一个音节;而作动词时 a-TRIB-ute 重音在第二个音节。

The following diagram shows the result of these assignments. A state diagram that shows an object and its attributes is called an object diagram; see Figure 15.1.

下面的图展示了这些赋值的结果。展示一个对象及其属性的状态图叫做 对象图;见图 15.1。

Figure 15.1: Object diagram.

图 15.1:对象图(原书插图未收录)

The variable Point refers to a Point object, which contains two attributes. Each attribute refers to a floating-point number.

变量 Point 引用一个 Point 对象,它包含两个属性。每个属性引用一个浮点数。

You can read the value of an attribute using the same syntax:

你可以用同样的语法读取属性的值:
Point0035

The expression Point00 means, “Go to the object Point refers to and get the value of x.” In this case, we assign that value to a variable named x. There is no conflict between the variable x and the attribute x.

表达式 Point00 的意思是:「找到 Point 引用的那个对象,取出 x 的值。」这里我们把这个值赋给名为 x 的变量。变量 x 和属性 x 之间并不冲突。

You can use dot notation as part of any expression. For example:

你可以在任何表达式里使用点号语法。例如:
Point0048

You can pass an instance as an argument in the usual way. For example:

你可以像往常一样把一个实例作为实参传入。例如:
Point0049

Point0050 takes a point as an argument and displays it in mathematical notation. To invoke it, you can pass Point as an argument:

Point0052 接收一个点作为实参,并用数学记法显示它。要调用它,可以把 Point 作为实参传入:
Point0054

Inside the function, x is an alias for Point, so if the function modifies x, Point changes.

在函数内部,xPoint 的别名,所以如果函数修改了 xPoint 也会变。

Exercise 1

习题 1

Write a function called Point0063 that takes two Points as arguments and returns the distance between them.

写一个名为 Point0064 的函数,接收两个 Point 作为实参,返回它们之间的距离。

15.3 Rectangles 15.3 矩形

Sometimes it is obvious what the attributes of an object should be, but other times you have to make decisions. For example, imagine you are designing a class to represent rectangles. What attributes would you use to specify the location and size of a rectangle? You can ignore angle; to keep things simple, assume that the rectangle is either vertical or horizontal.

有时对象该有哪些属性一目了然,但有时你得自己拿主意。比如,假设你正在设计一个表示矩形的类。你会用哪些属性来指定矩形的位置和大小?角度可以忽略;为了简单,假设矩形是水平或竖直的。

There are at least two possibilities:

至少有两种可能:

At this point it is hard to say whether either is better than the other, so we’ll implement the first one, just as an example.

目前还很难说哪种更好,所以我们就拿第一种来实现,权当示例。

Here is the class definition:

下面是类的定义:
Point0065

The docstring lists the attributes: Point and Point0 are numbers; Point0 is a Point object that specifies the lower-left corner.

文档字符串列出了属性:PointPoint0 是数字;Point0 是一个 Point 对象,表示左下角。

To represent a rectangle, you have to instantiate a Rectangle object and assign values to the attributes:

要表示一个矩形,你得实例化一个 Rectangle 对象,并给它的属性赋值:
Point0072

The expression Point0073 means, “Go to the object x00 refers to and select the attribute named Point0; then go to that object and select the attribute named x.”

表达式 Point0077 的意思是:「找到 x00 引用的对象,取出名为 Point0 的属性;再找到那个对象,取出名为 x 的属性。」

Figure 15.2: Object diagram.

图 15.2:对象图(原书插图未收录)

Figure 15.2 shows the state of this object. An object that is an attribute of another object is embedded.

图 15.2 展示了这个对象的状态。作为另一个对象的属性而存在的对象叫做 内嵌对象

15.4 Instances as return values 15.4 实例作为返回值

Functions can return instances. For example, Point0081 takes a Point0082 as an argument and returns a Point that contains the coordinates of the center of the Point0084:

函数可以返回实例。例如,Point0085 接收一个 Point0086 作为实参,返回一个 Point,其中含有该 Point0088 中心的坐标:
Point0089

Here is an example that passes x00 as an argument and assigns the resulting Point to Point0:

下面是一个例子,把 x00 作为实参传入,把得到的 Point 赋给 Point0:
Point0094

15.5 Objects are mutable 15.5 对象可变

You can change the state of an object by making an assignment to one of its attributes. For example, to change the size of a rectangle without changing its position, you can modify the values of Point and Point0:

你可以通过对某个属性赋值来改变对象的状态。例如,要在不改变位置的情况下改变矩形的大小,可以修改 PointPoint0 的值:
Point0099

You can also write functions that modify objects. For example, Point0100 takes a Rectangle object and two numbers, Point0 and Point01, and adds the numbers to the width and height of the rectangle:

你也可以写修改对象的函数。例如,Point0103 接收一个 Rectangle 对象和两个数字 Point0、Point01,把它们分别加到矩形的宽和高上:
Point0106

Here is an example that demonstrates the effect:

下面的例子展示了效果:
Point0107

Inside the function, x000 is an alias for x00, so if the function modifies x000, x00 changes.

在函数内部,x000 是 x00 的别名,所以如果函数修改了 x000,x00 也会变。

Exercise 2

习题 2

Write a function named Point0116 that takes a Rectangle and two numbers named x0 and x0. It should change the location of the rectangle by adding x0 to the x coordinate of Point0 and adding x0 to the x coordinate of Point0.

写一个名为 Point0125 的函数,接收一个 Rectangle 和两个名为 x0、x0 的数字。它应该通过在 Point0 的 x 坐标上加 x0、在 Point0 的 x 坐标上加 x0 来改变矩形的位置。

15.6 Copying 15.6 复制

Aliasing can make a program difficult to read because changes in one place might have unexpected effects in another place. It is hard to keep track of all the variables that might refer to a given object.

别名会让程序难以阅读,因为一处改动可能在另一处产生意外影响。很难追踪所有可能引用某个对象的所有变量。

Copying an object is often an alternative to aliasing. The x000 module contains a function called x000 that can duplicate any object:

复制对象常常是避免别名的一种办法。x000 模块里有一个名为 x000 的函数,可以复制任何对象:
Point0138

x0 and x0 contain the same data, but they are not the same Point.

x0 和 x0 含有相同的数据,但它们不是同一个 Point。
Point0143

The x0 operator indicates that x0 and x0 are not the same object, which is what we expected. But you might have expected x0 to yield x000 because these points contain the same data. In that case, you will be disappointed to learn that for instances, the default behavior of the x0 operator is the same as the x0 operator; it checks object identity, not object equivalence. This behavior can be changed—we’ll see how later.

x0 运算符说明 x0 和 x0 不是同一个对象,这在我们意料之中。但你也许会以为 x0 应该返回 x000,因为这两个点含有相同的数据。若如此,你会失望地发现:对实例而言,x0 运算符的默认行为和 x0 一样,它比较的是对象身份,而不是对象相等性。这个行为可以改——稍后我们会看到怎么做。

If you use Point0158 to duplicate a Rectangle, you will find that it copies the Rectangle object but not the embedded Point.

如果你用 Point0159 来复制一个 Rectangle,会发现它复制了 Rectangle 对象,却没有复制内嵌的 Point。
Point0160

Figure 15.3: Object diagram.

图 15.3:对象图(原书插图未收录)

Figure 15.3 shows what the object diagram looks like. This operation is called a shallow copy because it copies the object and any references it contains, but not the embedded objects.

图 15.3 展示了对象图的样子。这种操作叫做 浅拷贝,因为它复制对象以及它所包含的任何引用,却不复制内嵌的对象。

For most applications, this is not what you want. In this example, invoking Point0161 on one of the Rectangles would not affect the other, but invoking Point0162 on either would affect both! This behavior is confusing and error-prone.

对大多数应用来说,这不是你想要的。在这个例子中,对其中一个 Rectangle 调用 Point0163 不会影响另一个,但对任意一个调用 Point0164 却会同时影响两个!这种行为既让人困惑又容易出错。

Fortunately, the x000 module contains a method named Point016 that copies not only the object but also the objects it refers to, and the objects they refer to, and so on. You will not be surprised to learn that this operation is called a deep copy.

所幸,x000 模块里还有一个名为 Point016 的方法,它不仅复制对象,还复制它引用的对象,以及那些对象所引用的对象,依此类推。你不会惊讶地得知,这种操作叫做 深拷贝
Point0169

x000 and x00 are completely separate objects.

x000 和 x00 是完全独立的两个对象。

Exercise 3

习题 3

Write a version of Point0174 that creates and returns a new Rectangle instead of modifying the old one.

写一个 Point0175 的版本,它创建并返回一个新的 Rectangle,而不是修改原来的那个。

15.7 Debugging 15.7 调试

When you start working with objects, you are likely to encounter some new exceptions. If you try to access an attribute that doesn’t exist, you get an Point0176:

当你开始使用对象时,可能会遇到一些新的异常。如果你试图访问一个不存在的属性,就会得到 Point0177:
Point0178

If you are not sure what type an object is, you can ask:

如果你不确定一个对象是什么类型,可以问它:
Point0179

If you are not sure whether an object has a particular attribute, you can use the built-in function Point01:

如果你不确定一个对象是否具有某个属性,可以使用内置函数 Point01:
Point0182

The first argument can be any object; the second argument is a string that contains the name of the attribute.

第一个实参可以是任意对象;第二个实参是一个 字符串,包含属性的名字。

15.8 Glossary 15.8 术语表

class:
A user-defined type. A class definition creates a new class object.
class object:
An object that contains information about a user-defined type. The class object can be used to create instances of the type.
instance:
An object that belongs to a class.
attribute:
One of the named values associated with an object.
embedded (object):
An object that is stored as an attribute of another object.
shallow copy:
To copy the contents of an object, including any references to embedded objects; implemented by the x000 function in the x000 module.
deep copy:
To copy the contents of an object as well as any embedded objects, and any objects embedded in them, and so on; implemented by the Point018 function in the x000 module.
object diagram:
A diagram that shows objects, their attributes, and the values of the attributes.
class 类:
用户自定义的类型。类定义会创建一个新的类对象。
class object 类对象:
包含用户自定义类型信息的对象。类对象可用来创建该类型的实例。
instance 实例:
属于某个类的对象。
attribute 属性:
与对象关联的一个具名值。
embedded (object) 内嵌对象:
作为另一个对象的属性被存储的对象。
shallow copy 浅拷贝:
复制对象的内容,包括指向内嵌对象的引用;由 x000 模块中的 x000 函数实现。
deep copy 深拷贝:
复制对象的内容以及任何内嵌对象,还有内嵌对象里的内嵌对象,依此类推;由 x000 模块中的 Point019 函数实现。
object diagram 对象图:
展示对象、对象的属性以及属性取值的图。

15.9 Exercises 15.9 习题

Exercise 4

习题 4

Swampy (see Chapter 4) provides a module named Point, which defines a user-defined type also called Point. You can import it like this:

Swampy(见第 4 章)提供了一个名为 Point 的模块,它定义了一个同样叫 Point 的用户自定义类型。你可以这样导入它:
Point0195

Or, depending on how you installed Swampy, like this:

或者,取决于你安装 Swampy 的方式,也可以这样:
Point0196

The following code creates a World object and calls the Point019 method, which waits for the user.

下面的代码创建一个 World 对象,并调用 Point019 方法,它会等待用户操作。
Point0199

A window should appear with a title bar and an empty square. We will use this window to draw Points, Rectangles and other shapes. Add the following lines before calling Point020 and run the program again.

应该会弹出一个带标题栏和空白方框的窗口。我们会用这个窗口来画 Point、Rectangle 和其他图形。在调用 Point020 之前加上下面几行,再运行程序。
Point0202

You should see a green rectangle with a black outline. The first line creates a Canvas, which appears in the window as a white square. The Canvas object provides methods like Point0203 for drawing various shapes.

你应该会看到一个带黑色边框的绿色矩形。第一行创建了一个 Canvas,它在窗口里显示为一个白色方块。Canvas 对象提供了像 Point0204 这样的方法来绘制各种形状。

x000 is a list of lists that represents the “bounding box” of the rectangle. The first pair of coordinates is the lower-left corner of the rectangle; the second pair is the upper-right corner.

x000 是一个列表的列表,表示矩形的「包围盒」(bounding box)。第一对坐标是矩形的左下角;第二对是右上角。

You can draw a circle like this:

你可以这样画一个圆:
Point0207

The first parameter is the coordinate pair for the center of the circle; the second parameter is the radius.

第一个实参是圆心坐标对;第二个是半径。

If you add this line to the program, the result should resemble the national flag of Bangladesh (see http://en.wikipedia.org/wiki/Gallery_of_sovereign-state_flags).

如果加上这一行,结果应该类似孟加拉国国旗(见 http://en.wikipedia.org/wiki/Gallery_of_sovereign-state_flags)。
  1. Write a function called Point0208 that takes a Canvas and a Rectangle as arguments and draws a representation of the Rectangle on the Canvas.
  2. Add an attribute named Point to your Rectangle objects and modify Point0210 so that it uses the color attribute as the fill color.
  3. Write a function called Point0211 that takes a Canvas and a Point as arguments and draws a representation of the Point on the Canvas.
  4. Define a new class called Circle with appropriate attributes and instantiate a few Circle objects. Write a function called Point0212 that draws circles on the canvas.
  5. Write a program that draws the national flag of the Czech Republic. Hint: you can draw a polygon like this: Point0213
  1. 写一个名为 Point0214 的函数,接收 Canvas 和 Rectangle 作为实参,在 Canvas 上画出该 Rectangle 的表示。
  2. 给你的 Rectangle 对象加一个名为 Point 的属性,并修改 Point0216,让它把 color 属性用作填充色。
  3. 写一个名为 Point0217 的函数,接收 Canvas 和 Point 作为实参,在 Canvas 上画出该 Point 的表示。
  4. 定义一个名为 Circle 的新类,带有合适的属性,并实例化几个 Circle 对象。写一个名为 Point0218 的函数,在画布上画圆。
  5. 写一个程序画出捷克共和国国旗。提示:你可以这样画多边形:

I have written a small program that lists the available colors; you can download it from http://thinkpython.com/code/color_list.py.

我写了一个小程序,列出了可用的颜色;你可以从 http://thinkpython.com/code/color_list.py 下载。