Object-oriented programming permits us to organize our programs
around the interactions of objects. A
class provides the definition of the structure and
behavior of the objects; each object is an instance of a class. A typical
program is a number of class definitions and a final main function. The
main function creates the initial objects that will perform the job of the
program.
Object-oriented programming focuses software design and
implementation around the definitions of and interactions between
individual objects. An object is said to
encapsulate a state of being and a set of
behaviors; data and processing. Each instance of a class has individual
copies of the attributes which are tightly coupled with the class-wide
operations. We can understand objects by looking at four features,
adapted from [Rumbaugh91].
-
Identity. Each object is unique and is distinguishable from all other
objects. In the real world, two identical coffee cups occupy
different locations on our desk. In the world of a computer's
memory, objects could be identified by their address, which would
make them unique.
-
Classification. This is sometimes called
Encapsulation
.
Objects with the same attributes and behavior belong to a common
class. Each individual object has unique
values for the attributes. We saw this when we looked at the
various collection classes. Two different
list
objects have the same general
structure, and the same behavior. Both lists respond to
append
, pop
, and all of
the other methods of a list
. However, each
list
object has a unique sequence of
values.
-
Inheritance. A class can inherit methods from a parent class, reusing
common features. A superclass is more general, a subclass
overrides superclass features, and is more specific. With the
built-in Python classes, we've looked at the ways in which all
immutable sequences are alike.
-
Polymorphism. A general operation can have variant implementation methods,
depending on the class of the object. We saw this when we noted
that almost every class on Python has a
+
operation. Between two floating-point numbers the
+
operation adds the numbers, between two
lists, however, the
+
operation concatenates
the lists.
Python's Implementation. A class is the Python-language definition
of the features of individual objects: the
names of the attributes and definitions of the
operations. Generally, the attributes are the
instance variables for the object. The operations can also be called
methods or method functions. All Python objects are instances of some
class; the attributes of the object and methods that operate on the
object are specified in the class definition.
Python permits us to define our own classes of objects. Our
definition will provide a number of things.
-
We provide a distinct name to the class. We define the
behavior of each object by defining its method functions. The
attributes of each object are called instance variables and are
created when the object is created.
-
We list the superclasses from which a subclass inherits
features. This is called multiple inheritance and differs from the
single-inheritance approach used by languages like Java.
-
We provide method functions which define the operations for
the class.
-
We can define attributes as part of the class definition.
Generally, however, our attributes are simply instance variables,
which are created dynamically as the methods of the class are
evaluated. Most commonly, we provide an initialization method
function (named __init__
) which creates our
instance variables.
-
Python provides a simple version of unique identify. If
necessary, we can override this definition.
Note that our instance variables are not a formal part of the
class definition. This is because Python is a dynamic language; Python
doesn't rely on static declarations of instance variables the way C++ or
Java do.
Another consequence of Python's dynamic nature is that
polymorphism is based on simple matching of method names. This is
distinct from languages like Java or C++ where polymorphism depends on
inheritance. Python's approach is sometimes called “duck
typing”: if it quacks like a duck and walks like a duck it is a
duck. If several objects have the common method names, they are
effectively polymorphic with respect to those methods.
Interface and Attributes. The best programming practice is to treat each object as if the
internal implementation details where completely opaque. All other
objects within an application should use only the interface methods
and attributes for interacting with an object. Some languages (like
C++ or Java) have a formal distinction between interface and
implementation. Python has a limited mechanism for making a
distinction between the defined interface and the private
implementation of a class.
Python uses a few simple techniques for separating interface from
implementation. We can use a leading _
on an instance
variable or method function name to make it more-or-less private to the
class. Further, you can use properties or descriptors to create more
sophisticated protocols for accessing instance variables. We'll wait
until Chapter 25, Properties and Descriptors
to cover these more advanced
techniques.
Technically, a class definition creates a
class
object. This Python object defines the
class by containing the definitions of the various functions.
Additionally, a class object can also own class-level variables; these
are, in effect, attributes which are shared by each individual object of
that class.
An Object's Lifecycle. Each instance of every class has a lifecycle. The following is
typical of most objects.
-
Definition. The class definition is read by the Python interpreter or it
is built-in to the language. Our class definitions are created by
the
class
statement. Examples of built-in
classes include files, strings, sequences and mappings.
-
Construction. An instance of the class is constructed: Python allocates
the namespace for the object's instance variables and associating
the object with the class definition. The
__init__
method is executed to initialize any
attributes of the newly created instance.
-
Access and Manipulation. The instance's methods are called (similar to function calls
we covered in Chapter 9, Functions
), by client objects,
functions or the main script. There is a considerable amount of
collaboration among objects in most programs. Methods that report
on the state of the object are sometimes calles accessors; methods
that change the state of the object are sometimes called
manipulators.
-
Garbage Collection. Eventually, there are no more references to this instance.
Typically, the variable with an object reference was part of the
body of a function which finished, and the variables no longer
exist. Python detects this, and removes the object from memory,
freeing up the storage for subsequent reuse. This freeing of
memory is termed garbage collection, and
happens automatically. See Garbage Collection for more
information.