Added a section on multiple inheritance.

This commit is contained in:
Guido van Rossum 2001-07-11 19:09:28 +00:00
parent 9006318a33
commit 3294596182
1 changed files with 101 additions and 6 deletions

View File

@ -282,7 +282,7 @@ Making a type a factory for its instances
should also register the object with the garbage collection
subsystem if the type supports garbage collection. This slot
exists so that derived types can override the memory allocation
policy (e.g. which heap is being used) separately from the
policy (like which heap is being used) separately from the
initialization code. The signature is:
PyObject *tp_alloc(PyTypeObject *type, int nitems)
@ -368,9 +368,10 @@ Preparing a type for subtyping
The tp_free() slot should be used to free the memory and
unregister the object with the garbage collection subsystem, and
can be overridden by a derived class; tp_dealloc() should
deinitialize the object (e.g. by calling Py_XDECREF() for various
sub-objects) and then call tp_free() to deallocate the memory.
The signature for tp_dealloc() is the same as it always was:
deinitialize the object (usually by calling Py_XDECREF() for
various sub-objects) and then call tp_free() to deallocate the
memory. The signature for tp_dealloc() is the same as it always
was:
void tp_dealloc(PyObject *object)
@ -636,6 +637,94 @@ Subtyping in Python
dictionary (the third argument to the call to M).
Multiple Inheritance
The Python class statement supports multiple inheritance, and we
will also support multiple inheritance involving built-in types.
However, there are some restrictions. The C runtime architecture
doesn't make it feasible to have a meaningful subtype of two
different built-in types except in a few degenerate cases.
Changing the C runtime to support fully general multiple
inheritance would be too much of an upheaval of the code base.
The main problem with multiple inheritance from different built-in
types stems from the fact that the C implementation of built-in
types accesses structure members directly; the C compiler
generates an offset relative to the object pointer and that's
that. For example, the list and dictionary type structures each
declare a number of different but overlapping structure members.
A C function accessing an object expecting a list won't work when
passed a dictionary, and vice versa, and there's not much we could
do about this without rewriting all code that accesses lists and
dictionaries. This would be too much work, so we won't do this.
The problem with multiple inheritance is caused by conflicting
structure member allocations. Classes defined in Python normally
don't store their instance variables in structure members: they
are stored in an instance dictionary. This is the key to a
partial solution. Suppose we have the following two classes:
class A(dictionary):
def foo(self): pass
class B(dictionary):
def bar(self): pass
class C(A, B): pass
(Here, 'dictionary' is the type of built-in dictionary objects,
a.k.a. type({}) or {}.__class__ or types.DictType.) If we look at
the structure lay-out, we find that an A instance has the lay-out
of a dictionary followed by the __dict__ pointer, and a B instance
has the same lay-out; since there are no structure member lay-out
conflicts, this is okay.
Here's another example:
class X(object):
def foo(self): pass
class Y(dictionary):
def bar(self): pass
class Z(X, Y): pass
(Here, 'object' is the base for all built-in types; its structure
lay-out only contains the ob_refcnt and ob_type members.) This
example is more complicated, because the __dict__ pointer for X
instances has a different offset than that for Y instances. Where
is the __dict__ pointer for Z instances? The answer is that the
offset for the __dict__ pointer is not hardcoded, it is stored in
the type object.
Suppose on a particular machine an 'object' structure is 8 bytes
long, and a 'dictionary' struct is 60 bytes, and an object pointer
is 4 bytes. Then an X structure is 12 bytes (an object structure
followed by a __dict__ pointer), and a Y structure is 64 bytes (a
dictionary structure followed by a __dict__ pointer). The Z
structure has the same lay-out as the Y structure in this example.
Each type object (X, Y and Z) has a "__dict__ offset" which is
used to find the __dict__ pointer. Thus, the recipe for looking
up an instance variable is:
1. get the type of the instance
2. get the __dict__ offset from the type object
3. add the __dict__ offset to the instance pointer
4. look in the resulting address to find a dictionary reference
5. look up the instance variable name in that dictionary
Of course, this recipe can only be implemented in C, and I have
left out some details. But this allows us to use multiple
inheritance patterns similar to the ones we can use with classic
classes.
XXX I should write up the complete algorithm here to determine
base class compatibility, but I can't be bothered right now. Look
at best_base() in typeobject.c in the implementation mentioned
below.
XXX To be done
Additional topics to be discussed in this PEP:
@ -643,18 +732,24 @@ XXX To be done
- class methods and static methods
- mapping between type object slots (tp_foo) and special methods
(__foo__)
(__foo__) (actually, this may belong in PEP 252)
- built-in names for built-in types (object, int, str, list etc.)
- multiple inheritance restrictions
- method resolution order
- __dict__
- __slots__
- the HEAPTYPE and DYNAMICTYPE flag bits
- GC support
- API docs for all the new functions
- high level user overview
Implementation