Appendix C. Python Versus C++
This appendix briefly summarizes
some of the differences between Python and C++ classes.
Python's class system can be thought of as a
subset of C++'s. Although the comparison to Modula 3 may be
closer, C++ is the dominant OOP language today. But in Python, things
are intentionally simpler -- classes are simply
objects with attached
attributes that may have links to other class
objects. They support generation of multiple instances, customization
by attribute inheritance, and operator overloading, but the object
model in Python is comparatively uncluttered. Here are some specific
differences between Python and
C++:
- Attributes
-
There is no
real distinction between data members and methods in Python; both
simply designate named attributes of instances
or classes, bound to functions or other kinds of objects. Attributes
are names attached to objects, and accessed by qualification:
object.attribute. Methods are merely class
attributes assigned to functions normally created with nested
def statements; members are just attribute names
assigned to other kinds of objects.
- Class object generation
-
Class statements
create class objects and assign them to a name.
Statements that assign names within a class
statement generate class attributes, and classes inherit attributes
from all other classes listed in their class
statement header line (multiple inheritance is supported; this is
discussed in a moment).
- Instance object creation
-
Calling
a class object as though it were a function generates a new class
instance object. An instance begins with an
empty namespace that inherits names in the class's namespace;
assignments to instance attributes (e.g., to self
attributes within class method functions) create attributes in the
instance.
- Object deletion
-
Both
classes and instances (and any data they embed) are automatically
reclaimed when no longer held. There is no new
(classes are called instead) and Python's
del statement removes just one reference, unlike
C++'s delete.
- Member creation
-
Class and
instance attributes, like simple variables, spring into existence
when assigned, are not declared ahead of time, and may reference any
type of object (they may even reference different object datatypes at
different times).
- Inheritance
-
Python
inheritance is generally kicked off to search for an attribute
name's value: given an expression of the form
object.attribute, Python searches the namespace
object tree at object and above for the first
appearance of name attribute. An inheritance
search also occurs when expression operators and type operations are
applied to objects. A new, independent inheritance search is
performed for every object.attribute expression
that is evaluated -- even self.attr expressions
within a method function search anew for attr at
the instance object referenced by self and above.
- Runtime type information
-
Python classes are
objects in memory at runtime -- they can be
passed around a program to provide a sort of runtime type resource
(e.g., a single function can generate instances of arbitrary classes
passed in as an argument). Both class and instance objects carry
interpreter information (e.g., a __dict__
attribute dictionary), and Python's type
function allows object type testing. Instance objects'
__class__ attributes reference the class they
were created from, and class objects' __bases_
_ attributes give class superclasses (base classes).
- "this" pointer
-
Python's equivalent of the C++
this instance pointer is the first argument added
to method function calls (and usually called self
by convention). It is usually implicit in a call but is used
explicitly in methods: there is no hidden instance scope for
unqualified names. Python methods are just functions nested in a
class statement that receive the implied instance
objects in their leftmost parameters.
- Virtual methods
-
In Python, all methods and data members
are virtual in the C++ sense: there is no notion
of a compile-time resolution of attributes based on an object's
type. Every attribute qualification (object.name)
is resolved at runtime, based on the qualified object's type.
- Pure virtuals
-
Methods called by a superclass but not defined by it correspond to
C++'s concept of "pure virtual" methods: methods
that must be redefined in a subclass. Since Python is not statically
compiled, there is no need for C++'s special syntax to declare
this case. Calls to undefined methods raise a name error exception at
runtime, which may or may not be caught with try
statements.
- Static members
-
There is no
static class data declaration; instead,
assignments nested in a class statement generate
attribute names associated with the class and shared by all its
instances.
- Private members
-
There is no
notion of true access restrictions for attributes; every member and
method is public in the C++ sense. Attribute
hiding is a matter of convention rather than syntax: C++'s
public, private, and
protected constraints don't apply (but see
also the new __X class name localization feature
in Appendix A).
- Const interfaces
-
Objects
may be immutable, but names are not -- there is no equivalent of
C++'s const modifier. Nothing prevents a
name or object from being changed in a method, and methods can change
mutable arguments (the self object, for example).
Convention and common sense replaces extra syntax.
- Reference parameters
-
There
is no direct analogue for C++'s reference
parameters. Python methods may return multiple values in a tuple and
can change passed-in objects if they're mutable (for instance,
by assigning to an object's attributes or changing lists and
dictionaries in place). But there is no aliasing between names at the
call and names in a function header: arguments are passed by
assignment, which creates shared object references.
- Operator overloading
-
Special
method names overload operators: there is no operator+
-like syntax but the effects are similar. For instance, a
class attribute named __add__ overloads
(intercepts and implements) application of the +
operator to instances of the class; __getattr__
is roughly like C++ -> overloading. Arbitrary
expressions require coding right-side methods (e.g., _
_radd__ ).
- Templates
-
Python is
dynamically typed -- names are references to arbitrary objects,
and there is no notion of type declarations. C++ templates are
neither applicable nor necessary. Python classes and functions can
generally be applied to any object type that implements the interface
protocols (operations and operators) expected by the class's
code. Subjects need not be of a certain datatype.
- Friends
-
Everything is friendly in Python.
Because there is no notion of privacy constraints, any class can
access the internals of another.
- Function overloading
-
Python
polymorphism is based on virtual method calls: the type of a
qualified object determines what its methods do. Since Python
arguments' types are never declared (dynamically typed), there
is nothing like C++'s function overloading for dispatching to
different versions of a function based on the datatypes of its
arguments. You can explicitly test types and argument list lengths in
methods instead of writing separate functions for each type
combination (see type built-in function and
*args function argument form).
- Multiple inheritance
-
Multiple
inheritance is coded by listing more than one superclass in
parentheses in a class statement header line. When multiple
inheritance is used, Python simply uses the
first appearance of an attribute found during a
depth-first, left-to-right search through the superclass tree. Python
resolves multiple inheritance conflicts this way instead of treating
them as errors.
- Virtual inheritance
-
C++'s
notion of virtual base classes doesn't quite apply in Python. A
Python class instance is a single namespace dictionary (with a class
pointer for access to inherited attributes). Classes add attributes
to the class instance dictionary by assignment. Because of this
structure, each attribute exists in just one place -- the instance
dictionary. For inherited class attributes, the search of the
superclass tree resolves references unambiguously.
- Constructors
-
Python only runs the one _
_init__ method found by the inheritance object tree
search. It doesn't run all accessible classes'
constructors automatically; if needed, we have to call other class
constructors manually. But this is no harder than specifying
superclass constructor arguments in C++. Python destructors (
__del__ ) run when an instance is
garbage-collected (i.e., deallocated), not in response to
delete calls.
- Scope operators
-
C++
scope operators of the form Superclass::Method are
used to extend inherited methods and disambiguate inheritance
conflicts. Python's closest equivalent is
Superclass.Method, a class object qualification.
It isn't required for inheritance conflict resolution, but can
be used to override the default search rule and to call back to
superclasses in method extensions.
- Method pointers
-
Instead of
special syntax, Python method references are objects
; they may be passed, stored in data structures, and so
on. Method objects come in two flavors: bound
methods (when an instance is known) are instance/method pairs called
later like simple functions, and unbound methods
are simply references to a method function object and require an
instance to be passed explicitly when called.
Naturally, Python
has additional class features not found in C++, such as
metaclass protocols : __setattr_
_ can be used to implement alternative interfaces, and an
instance's __class__ pointer can be reset
to change the class type of an object dynamically. Moreover, class
attributes can be modified arbitrarily at runtime; classes are merely
objects with attached attribute names.
In addition, Python differs from C++ in
numerous ways besides its class model. For instance, there are
neither type declarations nor compile and linking steps in Python;
you cannot overload = in Python as you can in C++
(assignment isn't an operator in Python); and pointers, central
to much C and C++ programming, are completely absent in Python
(though object references can have some of the same effects). Instead
of pointers, Python programs use first-class objects, which are
automatically allocated and reclaimed.
Most of these differences stem from the fact that Python was designed
for speed of development, not speed of execution; much of C++'s
extra syntax would interfere with Python's purpose. See the
O'Reilly text Learning Python for a
complete introduction to Python classes and the remainder of the core
Python language.
|