Chapter 16
> 来源: Think Python 2e (Allen B. Downey)
> 原页: https://greenteapress.com/thinkpython/html/thinkpython016.html
\
------------------------------------------------------------------------
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.
15.1 User-defined types
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.
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.
There are several ways we might represent points in Python:
- We could store the coordinates separately in two variables,
xandx. - We could store the coordinates as elements in a list or tuple.
- We could create a new type to represent points as objects.
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.
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.
Point0014
Because Point is defined at the top level, its “full name” is Point0016 .
The class object is like a factory for creating objects. To create a Point, you call Point as if it were a function.
Point0018
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.
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).
15.2 Attributes
You can assign values to an instance using dot notation:
Point0021
This syntax is similar to the syntax for selecting a variable from a module, such as Point00 or Point0023 . In this case, though, we are assigning values to named elements of an object. These elements are called attributes.
As a noun, “AT-trib-ute” is pronounced with emphasis on the first syllable, as opposed to “a-TRIB-ute,” which is a verb.
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.
[插图缺失:thinkpython022.png]Figure 15.1: Object diagram.
The variable Point refers to a Point object, which contains two attributes. Each attribute refers to a floating-point number.
You can read the value of an attribute using the same syntax:
Point0025
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.
You can use dot notation as part of any expression. For example:
Point0032
You can pass an instance as an argument in the usual way. For example:
Point0033
Point0034 takes a point as an argument and displays it in mathematical notation. To invoke it, you can pass Point as an argument:
Point0036
Inside the function, x is an alias for Point, so if the function modifies x, Point changes.
Exercise 1
Write a function called Point0041 that takes two Points as arguments and returns the distance between them.
15.3 Rectangles
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:
- You could specify one corner of the rectangle (or the center), the width, and the height.
- You could specify two opposing corners.
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:
Point0042
The docstring lists the attributes: Point and Point0 are numbers; Point0 is a Point object that specifies the lower-left corner.
To represent a rectangle, you have to instantiate a Rectangle object and assign values to the attributes:
Point0046
The expression Point0047 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.”
[插图缺失:thinkpython023.png]Figure 15.2: Object diagram.
Figure 15.2 shows the state of this object. An object that is an attribute of another object is embedded.
15.4 Instances as return values
Functions can return instances. For example, Point0051 takes a Point0052 as an argument and returns a Point that contains the coordinates of the center of the Point0054:
Point0055
Here is an example that passes x00 as an argument and assigns the resulting Point to Point0:
Point0058
15.5 Objects are mutable
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:
Point0061
You can also write functions that modify objects. For example, Point0062 takes a Rectangle object and two numbers, Point0 and Point00, and adds the numbers to the width and height of the rectangle:
Point0065
Here is an example that demonstrates the effect:
Point0066
Inside the function, x000 is an alias for x00, so if the function modifies x000, x00 changes.
Exercise 2
Write a function named Point0071 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.
15.6 Copying
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:
Point0082
x0 and x0 contain the same data, but they are not the same Point.
Point0085
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.
If you use Point0093 to duplicate a Rectangle, you will find that it copies the Rectangle object but not the embedded Point.
Point0094
[插图缺失:thinkpython024.png]Figure 15.3: Object diagram.
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.
For most applications, this is not what you want. In this example, invoking Point0095 on one of the Rectangles would not affect the other, but invoking Point0096 on either would affect both! This behavior is confusing and error-prone.
Fortunately, the x000 module contains a method named Point009 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.
Point0099
x000 and x00 are completely separate objects.
Exercise 3
Write a version of Point0102 that creates and returns a new Rectangle instead of modifying the old one.
15.7 Debugging
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 Point0103 :
Point0104
If you are not sure what type an object is, you can ask:
Point0105
If you are not sure whether an object has a particular attribute, you can use the built-in function Point01:
Point0107
The first argument can be any object; the second argument is a string that contains the name of the attribute.
15.8 Glossary
- 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 thex000 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
Point011 function in thex000 module. - object diagram:
-
A diagram that shows objects, their attributes, and the values of the attributes.
15.9 Exercises
Exercise 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:
Point0114
Or, depending on how you installed Swampy, like this:
Point0115
The following code creates a World object and calls the Point011 method, which waits for the user.
Point0117
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 Point011 and run the program again.
Point0119
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 Point0120 for drawing various shapes.
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.
You can draw a circle like this:
Point0122
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 Point0123 ).
- Write a function called
Point0124 that takes a Canvas and a Rectangle as arguments and draws a representation of the Rectangle on the Canvas. - Add an attribute named
Pointto your Rectangle objects and modifyPoint0126 so that it uses the color attribute as the fill color. - Write a function called
Point0127 that takes a Canvas and a Point as arguments and draws a representation of the Point on the Canvas. - Define a new class called Circle with appropriate attributes and instantiate a few Circle objects. Write a function called
Point0128 that draws circles on the canvas. - Write a program that draws the national flag of the Czech Republic. Hint: you can draw a polygon like this:
Point0129
I have written a small program that lists the available colors; you can download it from Point0130 .
Contribute
If you would like to make a contribution to support my books, you can use the button below. Thank you!
Pay what you want:
Small $1.00 USD Medium $5.00 USD Large $10.00 USD X-Large $20.00 USD XX-Large $50.00 USD
Are you using one of our books in a class?
We'd like to know about it. Please consider filling out this short survey.
------------------------------------------------------------------------
\
---