More good stuff. Consider this just a checkpoint.

This commit is contained in:
Guido van Rossum 2001-06-14 20:48:43 +00:00
parent c051fdc214
commit 9c2e1292f6
1 changed files with 75 additions and 13 deletions

View File

@ -77,6 +77,21 @@ Introduction
checking of this flag bit. This should be fixed before the final
release.)
In current Python, a distinction is made between types and
classes. This PEP together with pep-0254 will remove that
distinction. However, for backwards compatibility there will
probably remain a bit of a distinction for years to come, and
without pep-0254, the distinction is still large: types ultimately
have a built-in type as a base class, while classes ultimately
derive from a user-defined class. Therefore, in the rest of this
PEP, I will use the word type whenever I can -- including base
type or supertype, derived type or subtype, and metatype.
However, sometimes the terminology necessarily blends, e.g. an
object's type is given by its __class__ attribute, and subtyping
in Python is spelled with a class statement. If further
distinction is necessary, user-defined classes can be referred to
as "classic" classes.
About metatypes
@ -258,6 +273,9 @@ Requirements for a type to allow subtyping
PyObject *args,
PyObject *kwds)
[XXX We'll have to rename tp_alloc to something else, because in
debug mode there's already a tp_alloc field.]
The arguments for tp_alloc are the same as for tp_new, described
above. The arguments for tp_init are the same except that the
first argument is replaced with the instance to be initialized.
@ -270,9 +288,9 @@ Requirements for a type to allow subtyping
cause a core dump or memory leakage this way.
Because tp_init is in a sense optional, tp_alloc is required to do
*some* initialization of the object. It is required to initialize
ob_refcnt to 1 and ob_type to its type argument. To be safe, it
should probably zero out the rest of the object.
*some* initialization of the object. It must initialize ob_refcnt
to 1 and ob_type to its type argument. It should zero out the
rest of the object.
The constructor arguments are passed to tp_alloc so that for
variable-size objects (like tuples and strings) it knows to
@ -488,20 +506,64 @@ Subtyping in Python
to be provided for the creation of C is: its name (in this example
the string "C"); the list of base classes (a singleton tuple
containing B); and the results of executing the class body, in the
form of a dictionary (e.g. {"var1": 1, "method1": <function...>,
...}).
form of a dictionary (e.g. {"var1": 1, "method1": <function
method1 at ...>, ...}).
According to the Don Beaudry hook, the following call is made:
I propose to rig the class statement to make the following call:
C = M("C", (B,), dict)
(where dict is the dictionary resulting from execution of the
class body). In other words, the metatype (M) is called. Note
that even though we currently require there to be exactly one base
class, we still pass in a (singleton) sequence of base classes;
this makes it possible to support multiple inheritance later (or
for types with a different metaclass!) without changing this
interface.
class body). In other words, the metatype (M) is called.
Note that even though we currently require there to be exactly one
base class, we still pass in a (singleton) sequence of base
classes; this makes it possible to support multiple inheritance
later (or for types with a different metaclass!) without changing
this interface.
In current Python, this is called the "Don Beaudry hook" after its
inventor; it is an exceptional case that is only invoked when a
base class is not a regular class. For a regular base class (or
when no base class is specified), current Python calls
PyClass_New(), the C level factory function for classes, directly.
I propose to change this so that Python *always* determines a
metaclass and calls it as given above. When one or more bases are
given, the type of the first base is used as the metatype;
when no base class is given, a default metaclass is chosen. By
setting the default metaclass to PyClass_Type, the metatype of
"classic" classes, the classic behavior of the class statement is
retained.
There are two further refinements here. First, a useful feature
is to be able to specify a metatype directly. If the class
statement defines a variable __metaclass__, that is the metatype
to call.
Second, with multiple bases, not all bases need to have the same
metatype. This is called a metaclass conflict [1]. Some
metaclass conflicts can be resolved by searching through the set
of bases for a metatype that derives from all other given
metatypes. If such a metatype cannot be found, an exception is
raised and the class statement fails.
This conflict resultion can be implemented in the metatypes
itself: the class statement just calls the metatype of the first
base, and this metatype's constructor looks for the most derived
metatype. If that is itself, it proceeds; otherwise, it calls
that metatype's constructor. (Ultimate flexibility: another
metatype might choose to require that all bases have the same
metatype, or that there's only one base class, or whatever.)
(Theoretically, it might be possible to automatically derive a new
metatype that is a subtype of all given metatypes; but since it is
questionable how conflicting method definitions of the various
metatypes should be merged, I don't think this is useful or
feasible. Should the need arise, the user can derive such a
metatype and specify it using the __metaclass__ variable. It is
also possible to have a new metatype that does this.)
HIRO
Note that calling M requires that M itself has a type: the
meta-metatype. In the current implementation, I have introduced a