Create a subfolder called lab10
within your
cs111
folder, and put all of the files for this lab in that
folder.
Begin by downloading
point.py,
saving it in your lab10
folder.
Open point.py
in Spyder. You’ll see that we’ve begun to define a
class called Point
that will serve as a blueprint for a new type
of object. Each Point
object represents a point with coordinates
(x,y)
in the Cartesian plane.
Locate the constructor function that we have provided for
Point
objects.
What is this function called? What does it do?
What are the names of the attributes that all Point
objects
have? How are those attributes defined?
Run point.py
in Spyder, and enter statements in the console to
create two Point
objects:
p1
, with coordinates (3, 4)p2
, with coordinates (-5, -12)Here is a template:
>>> p1 = # what should go here? >>> p2 = # what should go here?
Once you create your Point
objects, you should then be able
to do the following to see the values of their attributes:
>>> p1.x result: 3 >>> p1.y result: 4 >>> p2.x result: -5 >>> p2.y result: -12
By default, evaluating or printing an object will show you something that isn’t very helpful:
>>> p1 result: <__main__.Point object at 0x02DBED50> # you may get a different hexadecimal number after the word 'at'
To make it easier to display the current state of a Point
object,
copy the following method into your Point
class:
def __repr__(self): """ returns a string representation for the called Point object (self) """ s = '(' + str(self.x) + ', ' + str(self.y) + ')' return s
Make sure to put this method within the class definition, and indent it so that its header is aligned with the headers of the other methods that we’ve given you.
Once you have added this method, re-run point.py
in Spyder.
Like all __repr__
methods, this method returns a string
representation of an object. It will be called automatically
whenever a Point
object is printed, and whenever a Point
object
is evaluated from the console:
>>> p1 = Point(3, 4) >>> p1 # calls __repr__ on p1 result: (3, 4) >>> print(p1) # also calls __repr__ on p1 result: (3, 4)
Examine the method called distance_from_origin()
in point.py
.
It computes the distance of the called Point
object (self
)
from the origin (0,0)
.
Given the Point
object p1
that we created above, which of the
following calls would allow us to determine its distance from the
origin?
distance_from_origin(p1)
p1.distance_from_origin(3, 4)
p1.distance_from_origin()
Point.distanceFromOrigin(3, 4)
Point.distanceFromOrigin()
Experiment with the calls in the console as needed until you find which of the calls above works. Make sure you understand how the call works, and how the method is able to access the information that it needs to compute the distance.
By default, using the ==
operator to compare two objects compares
the memory addresses of the objects. It doesn’t compare the internals
of the objects. For example:
>>> p1 = Point(3, 4) >>> p2 = Point(3, 4) # a different Point object with the same coordinates >>> p1 == p2 # what do you see? False
To allow the ==
operator to compare the internals of the objects,
add an __eq__
method. It should have the following header:
def __eq__(self, other):
and it should return True
if the called Point
object (self
) has
the same coordinates as the Point
object other
, and False
otherwise. For example:
>>> p1 = Point(3, 4) >>> p2 = Point(3, 4) # a different Point object with the same coordinates >>> p3 = Point(3, 5) >>> p1 == p2 result: True >>> p1 == p3 result: False
Write a method called flip
that negates and swaps the coordinates
of the called Point
object. For example:
>>> p1 = Point(3, -5) >>> p1.flip() >>> p1 result: (5, -3) >>> p2 = Point(2, 8) >>> p2.flip() >>> p2 result: (-8, -2)
Why doesn’t flip()
need to return a value?
As discussed in lecture, a dictionary is built-in data structure that allows us to store key-value pairs that connect two types of data.
For example, we might want to have a dictionary that keeps track of course enrollment numbers – i.e., that connects the name of a course to the number of students enrolled in that course:
>>> enrolled = {'cs111': 500, 'cs112': 400, 'cs131': 250}
In the console, enter the above assignment statement so that you can use this dictionary in the rest of this task.
What are the keys in this dictionary? What are the values?
Given a dictionary, you can use a key as if it were an index to look up the associated value. For example, what expression would allow us to lookup the enrollment of cs111?
>>> ___________________________ # what expression is needed? result: 500
We can add a new key-value pair k:v
to a dictionary d
by
using an assignment of the form d[k] = v
Write an assignment statement that updates our enrolled
dictionary to include the fact that the enrollment of cs132 is
220.
>>> ___________________________ # what statement is needed? >>> enrolled result: {'cs111': 500, 'cs112': 400, 'cs131': 250, 'cs132': 220}
More generally, if d
is a dictionary and k
is one of its keys,
we can use an expression of the form d[k]
in the same ways that
we would use a variable.
Write a statement that updates our enrolled
dictionary to
increase the enrollment of cs112 by 10 students. (Important:
You should not assign 410. You should increase the current
enrollment by 10 using a statement that would work for any
existing enrollment value.)
>>> ___________________________ # what statement is needed? >>> enrolled result: {'cs111': 500, 'cs112': 410, 'cs131': 250, 'cs132': 220}
As discussed in lecture, we can also use other built-in functions and operators with dictionaries. What do each of the following expressions produce?
len(enrolled)
'cs131' in enrolled
'cs210' in enrolled
'cs210' not in enrolled
500 in enrolled
In PS 8, you will use a dictionary to build a Markov model of a text file. Let’s look at another possible use of a dictionary when processing on a text file: keeping track of all of the words that begin with a given letter.
For example, if we process a text file containing the following text:
Dictionaries are handy and helpful!
the corresponding dictionary should look something like this:
{'d': ['dictionaries'], 'a': ['are', 'and'], 'h': ['handy', 'helpful!']}
Each key-value pair in the dictionary has:
a key that is a single lowercase letter
a value that is a list containing all words from the text that begin with that letter (with any uppercase letters converted to lowercase):
Here are the steps you should take:
Download the following files:
Make sure that you save both of them in your lab10
folder.
Open lab10task3.py
in Spyder, and you will see the beginnings of
a function called build_dict
that takes the name of a text file as
its only input. It should create and return a dictionary of the
form described above that is based on the contents of the text file.
Review the provided code. What does it do?
Complete the body of the provided for
loop so that it makes the
appropriate changes to the dictionary d
for the current word
w
.
Notes:
The necessary steps will depend on whether the appropriate key
for w
is already in the dictionary, so you will
need an if-else
statement like the one that we used in lecture
when updating a dictionary.
Don’t forget that the values in this dictionary are lists, so your code will need to reflect that.
Use concrete cases to help you. For example, if w
is the
word 'committed'
, what line of code would you need if the
corresponding key is not already in d
? If the
corresponding key is already in d
?
After you have made the necessary changes, run lab10task3.py
in
Spyder and test your function using the edited_mission.txt
file
that you should have downloaded above. (This text file contains an
edited version of BU’s mission
statement.)
>>> d = build_dict('edited_mission.txt') >>> d['a'] result: ['a', 'and', 'an', 'amazing!'] >>> d['c'] result: ['comprehensive', 'committed', 'committed']
As time permits, the course staff will be giving you a chance to do some review for Midterm 2.
We encourage you to try these exercises later on your own.
In your Point
class, add a method called quadrant
that returns
the number of the quadrant (if any) in which the called Point
object (self
) falls. The method should return:
For example:
>>> p1 = Point(3, 4) >>> p1.quadrant() result: 1 >>> p2 = Point(-3, 4) >>> p2.quadrant() result: 2 >>> p3 = Point(-3, -4) >>> p3.quadrant() result: 3 >>> p4 = Point(3, -4) >>> p4.quadrant() result: 4 >>> p5 = Point(0, -4) >>> p5.quadrant() result: 0
Now add a method called in_same_quadrant
that takes another Point
object as a parameter and returns True
if the called Point
object
and the other Point
object are in the same quadrant, and False
otherwise. If one or both of the Point
objects are on an axis,
the method should return False
. This method should make use of
the quadrant
method to avoid code duplication.
For example:
>>> p1 = Point(3, 4) >>> p2 = Point(-3, 4) >>> p3 = Point(5, 12) >>> p1.in_same_quadrant(p2) result: False >>> p1.in_same_quadrant(p3) result: True >>> p4 = Point(3, 0) # on x-axis >>> p5 = Point(-2, 0) # also on x-axis >>> p4.in_same_quadrant(p5) result: False
Download the following client program of the Point
class:
and put it in your lab10
folder.
Open the newly downloaded file in Spyder. You’ll see that it
contains a program that creates and uses objects that are created
according to the blueprint provided by the Point
class. That’s
what we mean when we say that a program is a client of a class.
Examine the file, and try to determine what it’s supposed to do. Then go ahead and run the program in Spyder.
There are some bugs in the current version of the program that you will need to find and fix. Once you do so, you should get the following output:
p1 has coordinates (7, 24) p2 has coordinates (0, -7) (7, 24) is 25.0 units away from the origin (0, -7) is 7.0 units away from the origin moving p1 down 7 units... p1 now has coordinates (7, 17)
At the bottom of the client program from the previous problem, add code to do the following:
Get the x and y coordinates of a point from the user. Use 2
separate input
statements, one for the x coordinate and one
for the y coordinate. Use the int()
function to convert
the string values returned by input()
to integers, and
store those integers in variables.
Create a new Point
object with the coordinates specified by
the user, and store it in a variable called p3
.
Use a method call to determine the quadrant of p3
,
and report the result to the user.
Use a method call to determine the distance of p3
from the
origin, and report that distance to the user.
Use a method call to flip p3
, and then print its new
coordinates.
Use a method call to determine the new quadrant of p3
. If it
is on an axis, report that fact to the user. Otherwise, report
which quadrant p3
is in.
If time permits, add some additional client code that uses other
Point
methods on p3
.
Last updated on November 11, 2024.