a
decidedadvantage.
Using Classes
OOP is easy touse in Python, thanks largely to Python’s dynamic typing
model. In fact, it’s so easy that we’ll jump right into an example: Example 1-14 implements our
database records as class instances rather than as dictionaries.
Example 1-14. PP4E\Preview\person_start.py
class Person:
def __init__(self, name, age, pay=0, job=None):
self.name = name
self.age = age
self.pay = pay
self.job = job
if __name__ == '__main__':
bob = Person('Bob Smith', 42, 30000, 'software')
sue = Person('Sue Jones', 45, 40000, 'hardware')
print(bob.name, sue.pay)
print(bob.name.split()[-1])
sue.pay *= 1.10
print(sue.pay)
There is not much to this class—just a constructor method that
fills out the instance with data passed in as arguments to the class
name. It’s sufficient to represent a database record, though, and it can
already provide tools such as defaults for pay and job fields that
dictionaries cannot. The self-test code at the bottom of this file
creates two instances (records) and accesses their attributes (fields);
here is this file’s output when run under IDLE (a system command-line
works just as well):
Bob Smith 40000
Smith
44000.0
This isn’t a database yet, but we could stuff these objects into a
list or dictionary as before in order to collect them as a unit:
>>> from person_start import Person >>> bob = Person('Bob Smith', 42) >>> sue = Person('Sue Jones', 45, 40000) >>> people = [bob, sue] # a "database" list
>>> for person in people: print(person.name, person.pay) Bob Smith 0
Sue Jones 40000
>>> x = [(person.name, person.pay) for person in people] >>> x [('Bob Smith', 0), ('Sue Jones', 40000)]
>>> [rec.name for rec in people if rec.age >= 45] # SQL-ish query
['Sue Jones']
>>> [(rec.age ** 2 if rec.age >= 45 else rec.age) for rec in people] [42, 2025]
Notice that Bob’s pay defaulted to zero this time because we
didn’t pass in a value for that argument (maybe Sue is supporting him
now?). We might also implement a class that represents the database,
perhaps as a subclass of the built-in list or dictionary types, with
insert and delete methods that encapsulate the way the database is
implemented. We’ll abandon this path for now, though, because it will be
more useful to store these records persistently in a shelve, which
already encapsulates stores and fetches behind an interface for us.
Before we do, though, let’s add somelogic.
Adding Behavior
So far, ourclass is just data: it replaces dictionary keys with
object attributes, but it doesn’t add much to what we had before. To
really leverage the power of classes, we need to add some behavior. By
wrapping up bits of behavior in class method functions, we can insulate
clients from changes. And by packaging methods in classes along with
data, we provide a natural place for readers to look for code. In a
sense, classes combine records and the programs that process those
records; methods provide logic that interprets and updates the data (we
say they are object-oriented , because they always
process an object’s data).
For instance, Example 1-15 adds the last-name and raise logic as class methods; methods use the
self
argument to access or update the
instance (record) being processed.
Example 1-15. PP4E\Preview\person.py
class Person:
def __init__(self, name, age, pay=0, job=None):
self.name = name
self.age = age
self.pay = pay
self.job = job
def lastName(self):
return self.name.split()[-1]
def giveRaise(self, percent):
self.pay *= (1.0 + percent)
if __name__ == '__main__':
bob = Person('Bob Smith', 42, 30000, 'software')
sue = Person('Sue Jones', 45, 40000, 'hardware')
print(bob.name, sue.pay)
print(bob.lastName())
sue.giveRaise(.10)
print(sue.pay)
The output of this script is the same as the last, but the results
are being computed by methods now, not by hardcoded logic that appears
redundantly wherever it is