A Wikibookian has nominated this page for cleanup because: Copy editing needed You canhelp make it better. Please review anyrelevant discussion. |
We have used many of Python’s built-in types; now we are goingto define a new type. As an example, we will create a typecalledPoint that represents a point in two-dimensionalspace.
In mathematical notation, points are often written inparentheses with a comma separating the coordinates. For example,(0, 0) represents the origin, and (x,y) represents thepointx units to the right andy units up from the origin.
There are several ways we might represent points in Python:
Creating a new typeis (a little) more complicated than the other options, butit has advantages that will be apparent soon.
A user-defined type is also called aclass.A class definition looks like this:
classPoint(object):"""represents a point in 2-D space"""
This header indicates that the new class is aPoint,which is a kind ofobject, which is a built-intype.
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 namedPoint creates a class object.
>>>printPoint<class'__main__.Point'>
BecausePoint is defined at the top level, its “fullname” is__main__.Point.
The class object is like a factory for creating objects. To create aPoint, you callPoint as if it were a function.
>>>blank=Point()>>>printblank<__main__.Pointinstanceat0xb7e9d3ac>
The return value is a reference to a Point object, which weassign toblank. Creating a new object is calledinstantiation, and the object is aninstance ofthe class.
When you print an instance, Python tells you what class itbelongs to and where it is stored in memory (the prefix0x means that the following number is in hexadecimal).
You can assign values to an instance using dot notation:
>>>blank.x=3.0>>>blank.y=4.0
This syntax is similar to the syntax for selecting a variable from amodule, such asmath.pi orstring.whitespace. In this case,though, we are assigning values to named elements of an object.These elements are calledattributes.
As a noun, “AT-trib-ute” is pronounced with emphasis on the firstsyllable, 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 iscalled anobject diagram:
The variableblank refers to a Point object, whichcontains two attributes. Each attribute refers to afloating-point number.
You can read the value of an attribute using the same syntax:
>>>printblank.y4.0>>>x=blank.x>>>printx3.0
The expressionblank.x means, “Go to the objectblankrefers to and get the value ofx.” In this case, we assign thatvalue to a variable namedx. There is no conflict betweenthe variablex and the attributex.
You can use dot notation as part of any expression. For example:
>>>print'(%g,%g)'%(blank.x,blank.y)(3.0,4.0)>>>distance=math.sqrt(blank.x**2+blank.y**2)>>>printdistance5.0
You can pass an instance as an argument in the usual way.For example:
defprint_point(p):print'(%g,%g)'%(p.x,p.y)
print_point takes a point as an argument and displays it inmathematical notation. To invoke it, you can passblank asan argument:
>>>print_point(blank)(3.0,4.0)
Inside the function,p is an alias forblank, so ifthe function modifiesp,blank changes.
Write a function called 'distance' that it takes two Pointsas arguments and returns the distance between them.
Sometimes it is obvious what the attributes of an object should be,but other times you have to make decisions. For example, imagine youare designing a class to represent rectangles. What attributes wouldyou use to specify the location and size of a rectangle? You canignore angle; to keep things simple, assume that the rectangle iseither vertical or horizontal.
There are at least two possibilities:
At this point it is hard to say whether either is better thanthe other, so we’ll implement the first one, just as an example.
Here is the class definition:
classRectangle(object):"""represent a rectangle. attributes: width, height, corner. """
The docstring lists the attributes:width andheight are numbers;corner is a Point object thatspecifies the lower-left corner.
To represent a rectangle, you have to instantiate a Rectangleobject and assign values to the attributes:
box=Rectangle()box.width=100.0box.height=200.0box.corner=Point()box.corner.x=0.0box.corner.y=0.0
The expressionbox.corner.x means,“Go to the objectbox refers to and select the attribute namedcorner; then go to that object and select the attribute namedx.”
The figure shows the state of this object:
File:Book023.pngAn object that is an attribute of another object isembedded.
Functions can return instances. For example,find_centertakes aRectangle as an argument and returns aPointthat contains the coordinates of the center of theRectangle:
deffind_center(box):p=Point()p.x=box.corner.x+box.width/2.0p.y=box.corner.y+box.height/2.0returnp
Here is an example that passesbox as an argument and assignsthe resulting Point tocenter:
>>>center=find_center(box)>>>print_point(center)(50.0,100.0)
You can change the state of an object by making an assignment to one ofits attributes. For example, to change the size of a rectanglewithout changing its position, you can modify the values ofwidth andheight:
box.width=box.width+50box.height=box.height+100
You can also write functions that modify objects. For example,grow_rectangle takes a Rectangle object and two numbers,dwidth anddheight, and adds the numbers to thewidth and height of the rectangle:
defgrow_rectangle(rect,dwidth,dheight):rect.width+=dwidthrect.height+=dheight
Here is an example that demonstrates the effect:
>>>printbox.width100.0>>>printbox.height200.0>>>grow_rectangle(box,50,100)>>>printbox.width150.0>>>printbox.height300.0
Inside the function,rect is analias forbox, so if the function modifiesrect,box changes.
Write a function namedmove_rectangle that takes a Rectangle and two numbers nameddx anddy. It should change the location of the rectangle by addingdx to thex coordinate ofcorner and addingdy to they coordinate ofcorner.
Aliasing can make a program difficult to read because changesin one place might have unexpected effects in another place.It is hard to keep track of all the variables that might referto a given object.
Copying an object is often an alternative to aliasing.Thecopy module contains a function calledcopy thatcan duplicate any object:
>>>p1=Point()>>>p1.x=3.0>>>p1.y=4.0>>>importcopy>>>p2=copy.copy(p1)
p1 andp2 contain the same data, but they arenot the same Point.
>>>print_point(p1)(3.0,4.0)>>>print_point(p2)(3.0,4.0)>>>p1isp2False>>>p1==p2False
Theis operator indicates thatp1 andp2 are not thesame object, which is what we expected. But you might have expected== to yieldTrue because these points contain the samedata. In that case, you will be disappointed to learn that forinstances, the default behavior of the== operator is the sameas theis operator; it checks object identity, not objectequivalence. This behavior can be changed—we’ll see how later.
If you usecopy.copy to duplicate a Rectangle, you will findthat it copies the Rectangle object but not the embedded Point.
>>>box2=copy.copy(box)>>>box2isboxFalse>>>box2.cornerisbox.cornerTrue
Here is what the object diagram looks like:
This operation is called ashallow copy because it copies theobject and any references it contains, but not the embedded objects.
For most applications, this is not what you want. In this example,invokinggrow_rectangle on one of the Rectangles would notaffect the other, but invokingmove_rectangle on either wouldaffect both! This behavior is confusing and error-prone.
Fortunately, thecopy module contains a method nameddeepcopy that copies not only the object but also the objects it refers to, and the objectsthey refer to,and so on.You will not be surprised to learn that this operation iscalled adeep copy.
>>>box3=copy.deepcopy(box)>>>box3isboxFalse>>>box3.cornerisbox.cornerFalse
box3 andbox are completely separate objects.
Write a version ofmove_rectangle that creates and returns a new Rectangle instead of modifying the old one.
When you start working with objects, you are likely to encountersome new exceptions. If you try to access an attributethat doesn’t exist, you get anAttributeError:
>>>p=Point()>>>printp.zAttributeError:Pointinstancehasnoattribute'z'
If you are not sure what type an object is, you can ask:
>>>type(p)<type'__main__.Point'>
If you are not sure whether an object has a particular attribute,you can use the built-in functionhasattr:
>>>hasattr(p,'x')True>>>hasattr(p,'z')False
The first argument can be any object; the second argument is astring that contains the name of the attribute.
World.py, which is part of Swampy (see Chapter '4'), contains a class definition for a user-defined type calledWorld. If you run this code:fromWorldimport*world=World()wait_for_user()A window should appear with a title bar and an empty square. In this exercise we will use this window to draw Points, Rectangles and other shapes. Add the following lines beforewait_for_user and run the program again
''canvas=world.ca(width=500,height=500,background='white')bbox=[[-150,-100],[150,100]]canvas.rectangle(bbox,outline='black',width=2,fill='green4')
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 likerectangle for drawing various shapes.
bbox 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:
canvas.circle([-25,0],70,outline=None,fill='red')
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 (seewikipedia.org/wiki/Gallery_of_sovereign-state_flags).
draw_rectangle that takes a Canvas and a Rectangle as arguments and draws a representation of the Rectangle on the Canvas.draw_rectangle so that it uses the color attribute as the fill color.draw_point that takes a Canvas and a Point as arguments and draws a representation of the Point on the Canvas.draw_circle that draws circles on the canvas.points=[[-150,-100],[150,100],[150,-100]]canvas.polygon(points,fill='blue')
I have written a small program that lists the available colors; you can download it fromthinkpython.com/code/color_list.py.