Small rewordings based on Terry Reedy's feedback (finally).
Added a few more open issues.
This commit is contained in:
parent
17807b8b31
commit
aaebded223
101
pep-0253.txt
101
pep-0253.txt
|
@ -25,11 +25,11 @@ Introduction
|
|||
various flags, but most slots are pointers to functions to
|
||||
implement various kinds of behaviors. A NULL pointer means that
|
||||
the type does not implement the specific behavior; in that case
|
||||
the system may provide a default behavior in that case or raise an
|
||||
exception when the behavior is invoked. Some collections of
|
||||
function pointers that are usually defined together are obtained
|
||||
indirectly via a pointer to an additional structure containing
|
||||
more function pointers.
|
||||
the system may provide a default behavior or raise an exception
|
||||
when the behavior is invoked for an instance of the type. Some
|
||||
collections of function pointers that are usually defined together
|
||||
are obtained indirectly via a pointer to an additional structure
|
||||
containing more function pointers.
|
||||
|
||||
While the details of initializing a PyTypeObject structure haven't
|
||||
been documented as such, they are easily gleaned from the examples
|
||||
|
@ -171,41 +171,38 @@ Making a type a factory for its instances
|
|||
memory. As of Python 2.0, they also have to interface with the
|
||||
garbage collection subsystem, if the type chooses to participate
|
||||
in garbage collection (which is optional, but strongly recommended
|
||||
for so-called "container" types: types that may contain arbitrary
|
||||
references to other objects, and hence may participate in
|
||||
reference cycles).
|
||||
for so-called "container" types: types that may contain references
|
||||
to other objects, and hence may participate in reference cycles).
|
||||
|
||||
In this proposal, type objects can be factory functions for their
|
||||
instances, making the types directly callable from Python. This
|
||||
mimics the way classes are instantiated. Of course, the C APIs
|
||||
for creating instances of various built-in types will remain valid
|
||||
and probably the most common; and not all types will become their
|
||||
own factory functions.
|
||||
mimics the way classes are instantiated. The C APIs for creating
|
||||
instances of various built-in types will remain valid and in some
|
||||
cases more efficient. Not all types will become their own factory
|
||||
functions.
|
||||
|
||||
The type object has a new slot, tp_new, which can act as a factory
|
||||
for instances of the type. Types are made callable by providing a
|
||||
tp_call slot in PyType_Type (the metatype); the slot
|
||||
implementation function looks for the tp_new slot of the type that
|
||||
is being called.
|
||||
for instances of the type. Types are now callable, because the
|
||||
tp_call slot is set in PyType_Type (the metatype); the function
|
||||
looks for the tp_new slot of the type that is being called.
|
||||
|
||||
(Confusion alert: the tp_call slot of a regular type object (such
|
||||
as PyInt_Type or PyList_Type) defines what happens when
|
||||
*instances* of that type are called; in particular, the tp_call
|
||||
slot in the function type, PyFunction_Type, is the key to making
|
||||
functions callable. As another example, PyInt_Type.tp_call is
|
||||
NULL, because integers are not callable. The new paradigm makes
|
||||
*type objects* callable. Since type objects are instances of
|
||||
their metatype (PyType_Type), the metatype's tp_call slot
|
||||
(PyType_Type.tp_call) points to a function that is invoked when
|
||||
any type object is called. Now, since each type has do do
|
||||
something different to create an instance of itself,
|
||||
PyType_Type.tp_call immediately defers to the tp_new slot of the
|
||||
type that is being called. To add to the confusion, PyType_Type
|
||||
itself is also callable: its tp_new slot creates a new type. This
|
||||
is used by the class statement (via the Don Beaudry hook, see
|
||||
above). And what makes PyType_Type callable? The tp_call slot of
|
||||
*its* metatype -- but since it is its own metatype, that is its
|
||||
own tp_call slot!)
|
||||
Explanation: the tp_call slot of a regular type object (such as
|
||||
PyInt_Type or PyList_Type) defines what happens when *instances*
|
||||
of that type are called; in particular, the tp_call slot in the
|
||||
function type, PyFunction_Type, is the key to making functions
|
||||
callable. As another example, PyInt_Type.tp_call is NULL, because
|
||||
integers are not callable. The new paradigm makes *type objects*
|
||||
callable. Since type objects are instances of their metatype
|
||||
(PyType_Type), the metatype's tp_call slot (PyType_Type.tp_call)
|
||||
points to a function that is invoked when any type object is
|
||||
called. Now, since each type has do do something different to
|
||||
create an instance of itself, PyType_Type.tp_call immediately
|
||||
defers to the tp_new slot of the type that is being called.
|
||||
PyType_Type itself is also callable: its tp_new slot creates a new
|
||||
type. This is used by the class statement (formalizing the Don
|
||||
Beaudry hook, see above). And what makes PyType_Type callable?
|
||||
The tp_call slot of *its* metatype -- but since it is its own
|
||||
metatype, that is its own tp_call slot!
|
||||
|
||||
If the type's tp_new slot is NULL, an exception is raised.
|
||||
Otherwise, the tp_new slot is called. The signature for the
|
||||
|
@ -775,8 +772,8 @@ Method resolution order (the lookup rule)
|
|||
saved), defeating the whole purpose of inheriting from C in the
|
||||
first place.
|
||||
|
||||
Why was this not a problem in classic Python? Diamond diagrams is
|
||||
found rarely in classic Python class hierarchies. Most class
|
||||
Why was this not a problem in classic Python? Diamond diagrams
|
||||
are rarely found in classic Python class hierarchies. Most class
|
||||
hierarchies use single inheritance, and multiple inheritance is
|
||||
usually confined to mix-in classes. In fact, the problem shown
|
||||
here is probably the reason why multiple inheritance is impopular
|
||||
|
@ -892,22 +889,50 @@ XXX To be done
|
|||
|
||||
- built-in names for built-in types (object, int, str, list etc.)
|
||||
|
||||
- __dict__
|
||||
- __dict__ and dictoffset
|
||||
|
||||
- __slots__
|
||||
|
||||
- __dynamic__
|
||||
|
||||
- the HEAPTYPE and DYNAMICTYPE flag bits
|
||||
|
||||
- GC support
|
||||
|
||||
- API docs for all the new functions
|
||||
|
||||
- using __new__
|
||||
- how to use __new__
|
||||
|
||||
- writing metaclasses (using mro() etc.)
|
||||
|
||||
- high level user overview
|
||||
|
||||
- open issues:
|
||||
|
||||
- performance
|
||||
|
||||
- pickling, __reduce__
|
||||
|
||||
- do we need __coerce__, __del__?
|
||||
|
||||
- should we return to the old __getattr__ semantics, and
|
||||
introduce a new name (__getallattr__?) for the new semantics?
|
||||
or introduce a new name (__getattrhook__?)for the old
|
||||
semantics?
|
||||
|
||||
- whether __dynamic__ should be default
|
||||
|
||||
- assignment to __class__, __dict__, __bases__
|
||||
|
||||
- inconsistent naming
|
||||
(e.g. tp_dealloc/tp_new/tp_init/tp_alloc/tp_free)
|
||||
|
||||
- add builtin alias 'dict' for 'dictionary'?
|
||||
|
||||
- when subclasses of dict/list etc. are passed to system
|
||||
functions, the __getitem__ overrides (etc.) aren't always
|
||||
used
|
||||
|
||||
|
||||
Implementation
|
||||
|
||||
|
|
Loading…
Reference in New Issue