Lab 1: OOP
- Due before 23:59 Wednesday, August 31
- Submit using the submit program
as
301 lab 01
.
1 OOP Practice
Object-Oriented Programming is a really big part of implementing data structures, so it's important that we practice before our first Project.
Part One: A very simple class (30/100)
This class will be trivial in functionality, but will serve as an example
for class structure. In a file called lab1.py
, make a class called
Adding
. This class should have:
- A constructor (sometimes called an initializer in Python), which takes two arguments (they'll be integers), and stores them as fields in the class.
- A function called
add_third
, which takes an additional integer, and adds the three numbers together, before returning them (not printing them).
Copy-paste the following code into a file called lab1_test1.py
, in
the same directory as lab1.py
. Running python3 lab1_test1.py
should run your class correctly, and print the number 18:
1 2 3 4 5 6 | from lab1 import Adding if __name__ == "__main__": obj = Adding(5, 3) added = obj.add_third(10) print(added) |
Part Two: The __str__
Method (50/100)
Suppose we create a class called AClass
, and make an object of
that type, like so: obj = AClass()
. We then call
print(obj)
. What will print?
A right answer here is "I have no idea." The reason you have no idea is because you don't even know what data is stored within this object, so how can you possibly know what the right way to print it to the screen is? Python is in the same bind; this is your class, how can it know what you want to display?
Fortunately, there's a way of telling Python how to print things: the
__str__
function (special functions in Python usually start and
end with double-underscores, to avoid accidental name collisions). Whenever
an object needs to be printed, or otherwise converted to a string, Python goes
looking for that object's __str__
function; if it exists, it runs
that function, and that function returns a string (it does not print the
string, it just returns it); the returned string is then printed, or
used as the string representation.
Add a __str__
method to your Adding
class, so that when
an object of that type is printed, you get output that looks like this:
Adding: (firstnum, secondnum)
The following testing code:
1 2 3 4 5 | from lab1 import Adding if __name__ == "__main__": obj = Adding(3,9) print(obj) #Calls __str__ and prints what is returned |
should result in the following output:
$ python3 lab1_test2.py Adding: (3, 9)
Similarly, the following testing code:
1 2 3 4 5 6 7 | from lab1 import Adding if __name__ == "__main__": obj = Adding(3,9) strVersion = str(obj) #Calls __str__, and stores what is returned print('here') print(strVersion) |
Should result in the following output (notice how the object is printed AFTER 'here' due to the fact that __str__ returns, not prints, the string):
$ python3 lab1_test3.py here Adding: (3, 9)
2 Part Three: Keeping Track of Network Resources (100/100)
We're going to make a little program to keep track of which processes are running on a network and what their memory requirements are. Here's a description of the objects involved. A Process is an object which contains a name, and an int which indicates the number of KB of memory required to run that Process. A Machine is an object with a name, a list of Processes running on that Machine, and an int indicating the maximum amount of available memory in KB. A Network contains a list of Machines.
Description of Classes
Place all three of these classes in a single file named
network.py
. You can download a testing file here, which can help you test after finishing
each class. I won't usually give you a testing file like this, and will
require you to make your own, to have full confidence in what you're turning
in. Take note of how I wrote it!
Process has two fields, name
(a String) and
memoryReq
(an int). As far as methods, it should have:
- a constructor (sometimes called an initializer), which takes both a String and an int, in that order, and
__str__
, which returns a string of the formprocessName: memoryReqKB
(for example, "aName: 5KB")
Machine has three fields, name
(a String),
processList
(a list of Processes running on that machine), and
totalMem
(an int). For methods, it should have:
- a constructor, which takes a name and totalMem as arguments, and sets
processList
to be an empty list, addProcess
, which takes a Process as an argument, and appends it toprocessList
,availableMemory
, which takes no arguments (aside fromself
), and returns the result of subtracting the total amount of memory required by the Processes inprocessList
fromtotalMem
, and__str__
, which returns a string describing the machine of the formmachineName, totalMemKB aProcessName: someKB anotherProcessName: someOtherKB
Network has as a field a list of Machines named machines
.
For methods, it should have:
- a constructor, which takes no arguments (again, aside from
self
), and setsmachines
to be an empty list; addMachine
, which takes a Machine as an argument, and appends it to the listmachines
;addProcess
, which takes a Process as an argument, figures out which Machine inmachines
has the most available memory (by calling theiravailableMemory
method), and assigns the Process to that Machine (using itsaddProcess
method); and__str__
, which returns a string describing the entire network, of the formNetwork: machineName, totalMemKB aProcessName: someKB anotherProcessName: someOtherKB anotherMachineName, totalMemKB aProcessName: someKB anotherProcessName: someOtherKB
3 Optional Extensions
(Note the rest of this lab is an optional exercise for your enrichment, to challenge you and learn something and earn the admiration of your professor and peers. It might be tangibly useful if e.g. you are going to ask me to write you a recommendation letter at some point in the future, but mostly it'll just make you a better programmer!)
Opt1: Exceptions
There's one thing that can go wrong in the "Network" above, even if all of the arguments have the right types and would otherwise make sense: The network might not have any machine with enough memory for the process that you want to add!
This is a good example of where it would make sense to throw
a Python exception. So for this part, I want you to add an exception class
called OutOfMemory
, and make it so this exception is
thrown whenever the addProcess
method of Network
finds that there isn't enough memory in any machine to handle the situation.
It would probably be a good idea to make the same thing happen for the
addProcess
method in Machine
as well.
I'm not going to tell you exactly how to do this. Use Google, ask questions,
and think about the simplest and most straightforward way to do this
in Python. You'll definitely have to make a new class
for
your exception and put a throw
command somewhere.
Opt2: Placing multiple processes
Still want more to do? OK then!
Add one more method to the Network
class called
multiProcess
, which takes a list of processes as its
single argument, and adds all of them to various machines in the network.
One way to do this (and a good way to start) would be to add them one
at a time, using the addProcess
method you already wrote.
But this might not always lead to the very best way of using the memory
resources evenly. Can you think of anything better? Try it!