BPLCK105B-205B-module-5-pdf
BPLCK105B-205B-module-5-pdf
ython has a lot of built-in data types and we have used many of those types. Now we will try to
P
define our own data type that is called the user-defined (Programmer-defined) data type.
Example:
⮚
To understand how to create a user-defined data type, we consider an example, where we will create a
new type called Point that represents a point in 2-dimensional space.
I n mathematical terms, the coordinates of a point are written in parentheses with a comma separating
the coordinates.
For example, (10, 10) represents the point with x coordinate value is 10 and y is 10 and in general (x,
y) represents the point x units to the right and y units up from the origin.
.First, we could have stored the coordinates separatelyin two variables, say x and y.2.Second, we
1
could have stored the coordinates as elements in a list data structure or tuple data structure.
3.Third, we could have created a new type to representpoints as objects.
class Point:
he header indicates that the new class is created with the name Point. The body of the class is a
T
string that explains
what the class is defined for. You can also add variables and methods inside a class.
o create an object also called an instance of Point, you call Point as if it were a function.
T
Creating a new object (instance) is called instantiation, and the object is an instance of the
class.
blank = Point()
Attributes
ne can assign values to an instance using dot notation. The syntax is similar to the syntax for
O
selecting a variable from a module, such as math.piorstring.whitespace.Herewehavecreatedtwo
elements of an object and assigned values to the elements of an object. These elements are called
attributes of objects.
he state diagram that shows an object (instance) and its associated attributes are called an object
T
diagram which is shown below.
blank.x = 3.0
blank.y = 4.0
oint Object
P
The value of an element of an object can be displayed as shown below example. The two syntaxes are
shown with and without using a format specifiers.
utput
O
The value of element x is: 3.0
The value of element y is: 4.0
print ('The value of element x is %g \nThe value of element y is %g' % (blank.x, blank.y))
utput:
O
The value of element x is 3
The value of element y is 4
Rectangles
ometimesitisobviouswhattheattributesofanobjectshouldbe,butothertimesyouhavetomake
S
decisions. For example, imagine you want to design a class to represent rectangles. What are the
attributes you use to specify the corner of therectangleandthesizeofarectangle?assumethatthe
rectangle is either vertical or horizontal.
.
Syntax:
class Rectangle:
"""Represents a rectangle attributes: width, height, corner."""
⮚The docstring lists the attributes: width and height are numbers; corner is a Point object that
specifies the lower-left corner.
⮚To represent a rectangle, you have to instantiatea Rectangle object and assign values to the
attributes:
box = Rectangle()box.width = 100.0
box.height = 200.0 box.corner = Point()box.corner.x = 0.0
box.corner.y = 0.0
he expression box.corner.x means, “Go to the object box refers to and select the attributenamed
T
corner; then go to that object and select the attribute named x.”
igure 15.2 shows the state of this object. An object that is an attribute of another object is
F
embedded.
unctions can return instances. For example, find_center takes a Rectangle as an argument and
F
returns a Point That contains the coordinates of the center of the Rectangle:
Copying
liasingcanmakeaprogramdifficulttoreadbecausechangesinoneplacemighthaveunexpected
A
effects in another place. It is hard to keep track of all the variables that might refer to a given object.
opying an object is often an alternative to aliasing. The copy module contains a function called copy
C
that can duplicate any object:
>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
he is operator indicates that p1 and p2 are not the same object, which is what we ex- pected. But you
T
might have expected == to yield True because these points contain the samedata. In that case, you
will be disappointed to learn that for instance, the defaultbehavior of the == operator is the same as
the is operator; it checks object identity, notobject equivalence. That’s because for programmer
defined types, Python doesn’t know what should be considered equivalent. At least, not yet.
If you use copy.copy to duplicate a Rectangle, you will find that it copies the Rectangle object but
n ot the embedded Point.
>>> box2 = copy.copy(box)
>>> box2 is boxFalse
>>> box2.corner is box.cornerTrue
igure 15.3 shows what the object diagram looks like. This operation is called ashallow copy
F
because it copies the object and any references it contains, but not the embedded objects.
he copy module provides a method named deepcopythat copies not only the object but also the
T
objects it refers to, and the objectstheyrefer to, and so on. You will not be surprised to learn that
this operation is called adeep copy.
>>> box3 = copy.deepcopy(box)
>>> box3 is boxFalse
>>> box3.corner is box.cornerFalse
box3 and box are completely separate objects.
Classes and functions
Time:
s another example of a programmer-defined type, we’ll define a class called Time thatrecords
A
the time of day. The class definition looks like this:
class Time:
"""Represents the time of day.
T he two kinds of functions: pure functions and modifiers. They also demonstrate a development plan
I’ll callprototype and patch, which is a way of tacklinga complex problem by starting with a simple
prototype and incrementally dealing with the complications.
Here is a simple prototype of add_time:
d ef add_time(t1, t2):
sum = Time()
s um.hour = t1.hour + t2.hour
sum.minute = t1.minute + t2.minute
sum.second = t1.second + t2.second
return sum
he function creates a new Time object, initializes its attributes, and returns a reference to the
T
new object. This is called apure functionbecauseit does not modify any of the objectspassed to
it as arguments and it has no effect, like displaying a value or getting user input,other than
returning a value.
o test this function, we create two Time objects: start contains thestarttimeofamovie,like
T
MontyPythonandtheHolyGrail,anddurationcontainstheruntimeofthemovie,whichisone
hour 35 minutes.
Modifiers:
ometimes it is useful for a function to modify the objects it gets as parameters. In that case, the
S
changes are visible to the caller. Functions that work this way are calledmodifiers.
increment, which adds a given number of seconds to a Timeobject, can be written naturally as
a modifier. Here is a rough draft:
def increment(time, seconds):time.second += seconds
The first line performs the basic operation; the remainder deals with the special cases we saw
b efore.
Is this function correct? What happens if the second is much greater than sixty?
I n that case, it is not enough to carry once; we have to keep doing it until the time.second is less
than sixty. One solution is to replace the if statements with while statements. That would make
the function correct, but not very efficient. As an exercise, write a correct version of increment
that doesn’t contain any loops.
nything that can be done with modifiers can also be done with pure functions. In fact, some
A
programming languages only allow pure functions. There is some evidence thatprograms that
use pure functions are faster to develop and less error-prone than programs that use modifiers.
But modifiers are convenient at times, and functional programs tend tobe less efficient.
Prototyping versus planning
hedevelopmentplanIamdemonstratingiscalled“prototypeandpatch”.Foreachfunc-tion,I
T
wroteaprototypethatperformedthebasiccalculationandthentestedit,patchingerrorsalong
the way.
n alternative isdesigned development, in which high-levelinsight into the problem can make
A
the programming much easier.
In this case, the insight is that a Time object is really a three-digit number in base 60 (see
http://en.wikipedia.org/wiki/Sexagesimal). The second attribute is the “ones column”, the
minute attribute is the “sixties column”, and the hour attribute is the “thirty-six hundredths
column”.
hen we wrote add_time and increment, we were effectively doing addition in base 60,
W
which is why we had to carry from one column to the next.
his observation suggests another approach to the whole problem—we can convert Time
T
objectstointegersandtakeadvantageofthefactthatthecomputerknowshowtodointeger
arithmetic.
Here is a function that converts Times to integers:
def time_to_int(time):
inutes = time.hour * 60 + time.minute
m
seconds = minutes * 60 + time.second
return seconds
And here is a function that converts an integer to a Time (recall that divmod divides
the first argument by the second and returns the quotient and remainder as a tuple).
d ef
int_to_time(seconds):
time = Time()
inutes, time.second = divmod(seconds, 60)
m
time.hour, time.minute = divmod(minutes, 60)
return time
ou might have to think a bit, and run some tests, to convince yourself that these
Y
functionsare correct. One way to test them is tocheck that
time_to_int(int_to_time(x)) == x for many values of x. This is an example of a
consistency check.
nce you are convinced they are correct, you can use them to rewrite
O
add_time:def add_time(t1, t2):
bject-oriented features:
O
Python is anobject-oriented programming language, which means that it provides
features that support object-oriented programming, which has these defining characteristics:
∙Objects often represent things in the real world, and methods often correspond to the
ways things in the real world interact.
ethods: A method is a function that is associated with a particular class. We
M
haveseenmethodsforstrings,lists,dictionariesandtuples.Inthischapter,we will
define methods for programmer-defined types.
Methods are semantically the same as functions, but there are two syntactic differences:
• Methods are defined inside a class definition in order to make the relationship
between the class and the method explicit.
•The syntax for invoking a method is different fromthe syntax for calling a function.
I n thenextfewsections,wewilltakethefunctionsfromtheprevioustwochapters
and transform them into methods. This transformation is purely mechanical; you
can do itbyfollowingasequenceofsteps.Ifyouarecomfortableconvertingfrom
oneformtoanother,youwillbeabletochoosethebestformforwhateveryouare
doing.
Printing objects
We define a class named Time by using a functionnamedprint_time:
class Time:
"""Represents the time of day."""
def print_time(time):
' '
print( %.2d:%.2d:%.2d % (time.hour, time.minute,time.second))
To call this function, you have to pass a Timeobject as an argument:
>>> start = Time()
>>> start.hour = 9
I nside the method, the subject is assigned to the first parameter, so in this case start is
assigned to time.
y convention, the first parameter of a method is called self, so it would be more common
B
to write print_timelike this:
class Time:
def print_time(self):
' '
print( %.2d:%.2d:%.2d % (self.hour, self.minute,self.second))
The reason for this convention is an implicit metaphor:
⮚ The syntax for a function call, print_time(start), suggests that the function is the
a ctive agent. It says something like, “Hey print_time!Here’sanobjectforyouto
print.”
hischangeinperspectivemightbemorepolite,butitisnotobviousthatitisuseful.Inthe
T
exampleswehaveseensofar,itmaynotbe.Butsometimesshiftingresponsibilityfromthe
functions onto theobjectsmakesitpossibletowritemoreversatilefunctions(ormethods),
and makes it easier to maintain and reuse code.
sanexercise,rewritetime_to_int(fromSection16.4)asamethod.Youmightbetempted
A
to rewrite int_to_time as a method, too, but that doesn’t really make sense because there
would be no object to invoke it on.
Another example:
he subject, start, gets assigned to the first parameter, self. The argument, 1337, gets
T
assigned to the second parameter, seconds.
his mechanism can be confusing, especially if you make an error. For example,
T
if you invoke increment with two arguments, you get:
>> end = start.increment(1337, 460)
>
TypeError: increment() takes 2 positional arguments but 3 were given
The error message is initially confusing, because there are only two arguments in
p arentheses. But the subject is also considered an argument, so all together that’s three.
y the way, apositional argumentis an argument thatdoesn’t have a parameter
B
name;that is, it is not a keyword argument. In this function call:
sketch(parrot, cage, dead=True)
parrotand cageare positional, and deadis a keyword argument.
ewriting is slightly more complicated because it takes two Time objects as parameters.
R
In this case it is conventional to name the first parameter selfand the second parameter
other:
# inside class Time:
def__add__(self, other):
s econds = self.time_to_int() +
other.time_to_int()return int_to_time(seconds)
And here is how you could use it:
>>> start = Time(9, 45)
>>> duration = Time(1, 35)
>> print(start + duration)
>
11:20:00
Type-based dispatch:
I n the previous section we added two Timeobjects,butyoualsomightwanttoaddan
integer to a Time object. The following is a version of __add__ that checks the type of
otherand invokes either add_timeor increment:
# inside class Time:
else:
return self.increment(other)
I f other is a Time object, __add__ invokes add_time. Otherwise it assumes that the param-
eter is a number and invokes increment. This operation is called atype-based dispatch
because it dispatches the computation to different methods based on the type of the
argu-ments.
Polymorphism:
ype-based dispatch is useful when it is necessary, but (fortunately) it is not always
T
necessary. Often you can avoid it by writing functions that work correctly for arguments
with different types.
anyofthefunctionswewroteforstringsalsoworkforothersequencetypes.Forexam-
M
ple, in Section 11.2 weusedhistogramtocountthenumberoftimeseachletterappearsin
word.
d ef histogram(s): d =
dict() for c in s:
if c not in d:d[c] = 1
else:
d[c] = d[c]+1
return d
his function also works for lists, tuples, and even dictionaries, as long as the elements of
T
sare hashable, so they can be used as keys in d.
' ' ' ' ' ' ' ' ' ' ' '
>>> t = [ spam , egg , spam , spam , bacon , spam ]
>>> histogram(t)
' ' ' ' ' '
{ bacon : 1, egg : 1, spam : 4}
unctions that work with several types are called polymorphic. Polymorphism can
F
facilitatecodereuse.Forexample,thebuilt-infunctionsum,whichaddstheelementsof a
sequence, works as long as the elements of the sequence support addition.
Since Time objects provide an addmethod, they work with sum:
>>> t1 = Time(7, 43)
>>> t2 = Time(7, 41)
>>> t3 = Time(7, 37)
>>> total = sum([t1, t2, t3])
>> print(total)
>
23:01:00
I n general, if all of the operations inside a function work with a given type, the function
works with that type.
he best kind of polymorphism is the unintentional kind, where you discover that a
T
function you already wrote can be applied to a type you never planned for.
Interface and implementation:
neofthegoalsofobject-orienteddesignistomakesoftwaremoremaintainable, which
O
means that youcankeeptheprogramworkingwhenotherpartsofthesystemchange, and
modify the program to meet new requirements.
or example, in this chapter we developed a class that represents a time of day. Methods
F
provided by this class include time_to_int, is_after, and add_time.
e could implement those methods in several ways. The details of the implementation
W
depend on how we represent time. Inthischapter,theattributesofaTimeobjectarehour,
minute, and second.
s an alternative, we couldreplacetheseattributeswithasingleintegerrepresentingthe
A
number of seconds since midnight. This implementation would make some methods,
likeis_after, easier to write, but it makes other methods harder.
Afteryoudeployanewclass,youmightdiscoverabetterimplementation.Ifotherpartsof
theprogramareusingyourclass,itmightbetime-consuminganderror-pronetochangethe
interface.
ut if you designed the interface carefully, you can change the implementation without
B
changing the interface, which means that other parts of the program don’t have to change.