More good stuff. Consider this just a checkpoint.
This commit is contained in:
parent
c051fdc214
commit
9c2e1292f6
88
pep-0253.txt
88
pep-0253.txt
|
@ -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)
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue