Διάλεξη 5ης Μαΐου 2015 - Κλάσεις και αντικείμενα
Παραθέτουμε μερικά ακόμα χρήσιμα παραδείγματα προγραμμάτων που χρησιμοποιούν την τεχνική του αντικειμενοστραφούς προγραμματισμού. Στο πρώτο από αυτά αναπτύσσουμε ένα πρόγραμμα που θα μπορούσε να χρησιμοποιηθεί από μια εταιρεία διαχείρισης μεταχειρισμένων αυτοκινήτων. Θα υποθέσουμε ότι τα αυτοκίνητα της εταιρείας χωρίζονται σε τρεις τύπους: επιβατικά, φορτηγά και οχήματα τύπου SUV. Άσχετα με τον τύπο, η εταιρεία καταγράφει τα εξής δεδομένα για κάθε όχημα: μάρκα, έτος κυκλοφορίας, χιλιόμετρα που έχει διανύσει και τιμή. Ειδικά για τα επιβατικά αυτοκίνητα η εταιρεία καταγράφει των αριθμό των θυρών (2 ή 4), για τα φορτηγά τον τύπο μετάδοσης της κίνησης και για τα SUV την χωρητικότητα των επιβατών. Η τακτική που θα ακολουθήσουμε θα είναι η εξής: θα γράψουμε την κλάση Vehicle (όχημα) και της υποκλάσεις Car, Truck και SUV για κάθε συγκεκριμένο τύπο οχήματος οι οποίες κληρονομούν από την κλάση Vehicle τα κοινά χαρακτηριστικά κάθε οχήματος.
class Vehicle:
def __init__(self, make, model, mileage, price):
self.make = make
self.model = model
self.mileage = mileage
self.price = price
def getMake(self):
return self.make
def getModel(self):
return self.model
def getMileage(self):
return self.mileage
def setPrice(self, price):
self.price = price
def getPrice(self):
return self.price
Ορίζουμε τώρα την υποκλάση Car (επιβατικό αυτοκίνητο) ως υποκλάση της Vehicle και προσθέτουμε το χαρακτηριστικό του αριθμού των θυρών:
class Car(Vehicle):
def __init__(self, make, model, mileage, price, doors):
Vehicle.__init__(self, make, model, mileage, price)
self.doors = doors
def getDoors(self):
return self.doors
Με τον ίδιο τρόπο, ορίζουμε τις κλάσεις Truck και SUV, προσθέτοντας κάθε φορά τα ιδιαίτερα χαρακτηριστικά τους:
class Truck(Vehicle):
def __init__(self, make, model, mileage, price, drive):
Vehicle.__init__(self, make, model, mileage, price)
self.drive = drive
def getDrive(self):
return self.drive
class SUV(Vehicle):
def __init__(self, make, model, mileage, price, seats):
Vehicle.__init__(self, make, model, mileage, price)
self.seats = seats
def getSeats(self):
return self.seats
Ένα παράδειγμα χρήσης των κλάσεων αυτών φαίνεται παρακάτω:
bmw = Car("BMW", 2001, 7000, 15000.0, 4)
toyota = Truck("Toyota', 2000, 40000, 12000.0, "4WD")
volvo = SUV('Volvo', 2001, 30000, 18500.0, 5)
print 'Make: ' + car.getMake()
print 'Year: ' + car.getModel()
print 'Miles: ' + car.getMileage()
print 'Doors: ' + car.getDoors()
print 'Price: ' + getPrice()
Σαν δεύτερο παράδειγμα, αναπτύσσομε κλάσεις που θα μπορούσαν χρησιμοποιηθούν για την διαχείριση μιας μουσικής συλλογής.
class Song:
def __init__(self, title, artist, album, track):
self.title = title
self.artist = artist
self.album = album
self.track = track
artist.addSong(self)
class Album:
def __init__(self, title, artist, year):
self.title = title
self.artist = artist
self.year = year
self.tracks = []
srtist.addAlbum(self)
def addTrack(self, title, artist=None):
if artist is None:
artist = self.artist
numtracks = len(self.trucks)
song = Song(title, title, artist, self, numtracks)
self.tracks.append(song)
class Artist:
def __init__(self, name):
self.name = name
self.albums = []
self.songs = []
def addAlbum(self, album):
self.albums.append(album)
def addSong(self, song):
self.songs.append(song)
class Playlist:
def __init__(self, name):
self.name = name
self.songs = []
def addSong(self, song):
self.songs.append(song)
band = Artist("Bob's Awesome Band")
album = Album("Bob's First Single", band, 2013)
album.add_track("A Ballad about Cheese")
album.addTrack("A Ballad about Cheese (dance remix)")
album.addTrack("A Third Song to Use Up the Rest of the Space")
playlist = Playlist("My Favourite Songs")
for song in album.tracks:
playlist.add_song(song)
Το πρόβλημα του σακιδίου
Ένας διαρρήκτης έχει ένα σακίδιο το οποίο χωράει 20 κιλά. Σκοπός του είναι κάθε φορά που ληστεύει ένα σπίτι να παίρνει μαζί του τα πολυτιμότερα αντικείμενα χωρίς το συνολικό βάρος τους να υπερβαίνει την χωρητικότητα του σακιδίου. Ας υποθέσουμε για παράδειγμα ότι έχει να επιλέξει μεταξύ των παρακάτω αντικειμένων (δίπλα στο όνομα κάθε αντικειμένου αναγράφεται η αξία του, το βάρος του και η αξία του αντικειμένου ανά μονάδα βάρους):
- Clock, 175, 10, 17.5
- Painting, 90, 9, 10
- Radio, 20, 4, 5
- Vase, 50, 2, 25
- Book, 10, 1, 10
- Computer, 200, 20, 10
Η ερώτηση είναι, βεβαίως, ποιά από τα παραπάνω αντικείμενα πρέπει να επιλέξει έτσι ώστε να μεγιστοποιήσει την αξία των κλοπιμαίων και χωρίς να υπερβεί την χωρητικότηατα του σακιδίου του. Ο διαρρήκτης μπορεί να καταστρώσει την εξής στρατηγική για την επιλογή των κλοπιμαίων που θα βάλει στο σακίδιό του: να επιλέξει πρώτα το "καλύτερο" αντικείμενο, στη συνέχεια το δεύτερο "καλύτερο" αντικείμενο κ.ο.κ., μέχρι να εξαντλήσει τη χωρητικότητα του σακιδίου. Πρέπει, φυσικά, να επιλέξει την έννοια του όρου "καλύτερο": είναι το αντικείμενο με την μεγαλύτερη αξία, το αντικείμενο με το μικρότερο βάρος ή το αντικείμενο με τον μεγαλύτερο λόγο αξίας προς μονάδα βάρους; Μπορούμε να γράψουμε μερικές γραμμές κώδικα οι οποίες θα βοηθήσουν το διαρρήκτη να αποφασίσει:
class Item:
def __init__(self, name, value, weight):
self.name = name
self.value = float(value)
self.weight = float(weight)
def getName(self):
return self.name
def getValue(self):
return self.value
def getWeight(self):
return self.weight
def __str__(self):
return self.name + ', ' + str(self.value) + ', ' + str(self.weight)
def value(item):
retrun item.getValue()
def weightInverse(item):
return 1.0 / item.getWeight()
def ratio(item):
return item.getValue() / item.getWeight()
def buildItems():
name = ['clock', 'painting', 'radio', 'vase', 'book', 'computer']
values = [175, 90, 20, 50, 10, 200]
weights = [10, 9, 4, 2, 1, 20]
Items = []
for i in range(len(values)):
Items.append( Item(names[i], values[i], weights[i]) )
return Items
Μπορούμε τώρα να εξετάσουμε ποιά από τις τρεις στρατηγικές που αναφέραμε παραπάνω θα είναι η περισσότερο επικερδής για το διαρρήκτη:
def greedy(Items, maxWeight, keyFcn):
ItemsCopy = sorted(Items, key=keyFcn, reverse=True)
totalValue = 0.0
totalWeight = 0.0
result = []
i = 0
while totalWeight < maxWeight and i < len(Items):
if totalWeight + ItemsCopy[i].getWeight() <= maxWeight:
result.append(ItemsCopy[i])
totalWeight += ItemsCopy[i].getWeight()
totalValue += ItemsCopy[i].getValue()
i += 1
return (result, totalVal)
def testGreedy(Items, constraint, getKey):
taken, val = greedy(Items, constraint, getKey)
print 'Total value of items = ' + str(val)
for item in taken:
print item
def testAll(maxWeight = 20):
Items = buildItems()
print 'use value strategy'
testGreedy(Items, maxWeight, value)
print 'use inverse weight strategy'
testGreedy(Items, maxWeight, weightInverse)
print 'use value per unit weight strategy'
testGreedy(Items, maxWeight, ratio)
Αν καλέσουμε την συνάρτηση testAll()
τα αποτελέσματα θα είναι τα
ακόλουθα:
use value strategy
Total value of items = 200.0
computer, 200.0, 20.0
use inverse weight strategy
Total value of items = 170.0
book, 10.0, 1.0
vase, 50.0, 2.0
radio, 20.0, 4.0
painting, 90.0, 9.0
use value per unit weight strategy
Total value of items = 255.0
vase, 50.0, 2.0
clock, 175.0, 10.0
book, 10.0, 1.0
radio, 20.0, 4.0