Fixed a number of typos, and made some semantic updates in a few areas I
was sure about.
This commit is contained in:
parent
0bc40d7a97
commit
a307f83a41
99
pep-0253.txt
99
pep-0253.txt
|
@ -107,7 +107,7 @@ About metatypes
|
|||
<type 'type'>
|
||||
>>> type(type(type(a)))
|
||||
<type 'type'>
|
||||
>>>
|
||||
>>>
|
||||
|
||||
In this example, type(a) is a "regular" type, and type(type(a)) is
|
||||
a metatype. While as distributed all types have the same metatype
|
||||
|
@ -146,7 +146,7 @@ About metatypes
|
|||
example, Python code will never be allowed to allocate raw memory
|
||||
and initialize it at will.)
|
||||
|
||||
Metatypes determine various *policies* for types,such as what
|
||||
Metatypes determine various *policies* for types, such as what
|
||||
happens when a type is called, how dynamic types are (whether a
|
||||
type's __dict__ can be modified after it is created), what the
|
||||
method resolution order is, how instance attributes are looked
|
||||
|
@ -223,13 +223,13 @@ Making a type a factory for its instances
|
|||
reference to an existing object is fine too. The return value
|
||||
should always be a new reference, owned by the caller.
|
||||
|
||||
One the tp_new slot has returned an object, further initialization
|
||||
Once the tp_new slot has returned an object, further initialization
|
||||
is attempted by calling the tp_init() slot of the resulting
|
||||
object's type, if not NULL. This has the following signature:
|
||||
|
||||
PyObject *tp_init(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
int tp_init(PyObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
|
||||
It corresponds more closely to the __init__() method of classic
|
||||
classes, and in fact is mapped to that by the slot/special-method
|
||||
|
@ -237,7 +237,7 @@ Making a type a factory for its instances
|
|||
the tp_new() slot and the tp_init() slot lies in the invariants
|
||||
they ensure. The tp_new() slot should ensure only the most
|
||||
essential invariants, without which the C code that implements the
|
||||
object's would break. The tp_init() slot should be used for
|
||||
objects would break. The tp_init() slot should be used for
|
||||
overridable user-specific initializations. Take for example the
|
||||
dictionary type. The implementation has an internal pointer to a
|
||||
hash table which should never be NULL. This invariant is taken
|
||||
|
@ -272,6 +272,10 @@ Making a type a factory for its instances
|
|||
object; in this case tp_new() should always return a new object
|
||||
(or raise an exception).
|
||||
|
||||
Both tp_new() and tp_init() should receive exactly the same 'args'
|
||||
and 'kwds' arguments, and both should check that the arguments are
|
||||
acceptable, because they may be called independently.
|
||||
|
||||
There's a third slot related to object creation: tp_alloc(). Its
|
||||
responsibility is to allocate the memory for the object,
|
||||
initialize the reference count (ob_refcnt) and the type pointer
|
||||
|
@ -291,7 +295,7 @@ Making a type a factory for its instances
|
|||
|
||||
type->tp_basicsize + nitems * type->tp_itemsize
|
||||
|
||||
This slot is only used for subclassable types. The tp_new()
|
||||
The tp_alloc slot is only used for subclassable types. The tp_new()
|
||||
function of the base class must call the tp_alloc() slot of the
|
||||
type passed in as its first argument. It is the tp_new()
|
||||
function's responsibility to calculate the number of items. The
|
||||
|
@ -303,13 +307,6 @@ Making a type a factory for its instances
|
|||
counters for the number of allocations and deallocations. These
|
||||
are renamed to tp_allocs and tp_deallocs.)
|
||||
|
||||
XXX The keyword arguments are currently not passed to tp_new();
|
||||
its kwds argument is always NULL. This is a relic from a previous
|
||||
revision and should probably be fixed. Both tp_new() and
|
||||
tp_init() should receive exactly the same arguments, and both
|
||||
should check that the arguments are acceptable, because they may
|
||||
be called independently.
|
||||
|
||||
Standard implementations for tp_alloc() and tp_new() are
|
||||
available. PyType_GenericAlloc() allocates an object from the
|
||||
standard heap and initializes it properly. It uses the above
|
||||
|
@ -332,7 +329,7 @@ Preparing a type for subtyping
|
|||
structure (but must leave the names, order and type of the members
|
||||
of the base structure unchanged) and can override certain slots in
|
||||
the type object, leaving others the same. (Unlike C++ vtables,
|
||||
all Python type objects have the same memory lay-out.)
|
||||
all Python type objects have the same memory layout.)
|
||||
|
||||
The base type must do the following:
|
||||
|
||||
|
@ -357,7 +354,7 @@ Preparing a type for subtyping
|
|||
It should come as no surprise that there are similar conventions
|
||||
at the end of an object's lifetime. The slots involved are
|
||||
tp_dealloc() (familiar to all who have ever implemented a Python
|
||||
extension type) and tp_free(), the new kid on he block. (The
|
||||
extension type) and tp_free(), the new kid on the block. (The
|
||||
names aren't quite symmetric; tp_free() corresponds to tp_alloc(),
|
||||
which is fine, but tp_dealloc() corresponds to tp_new(). Maybe
|
||||
the tp_dealloc slot should be renamed?)
|
||||
|
@ -398,7 +395,12 @@ Preparing a type for subtyping
|
|||
functions that require an instance of the base type may be invoked
|
||||
with instances of the derived type. Before enabling subtyping of
|
||||
a particular type, its code should be checked to make sure that
|
||||
this won't break anything.
|
||||
this won't break anything. It has proved useful in the prototype
|
||||
to add another type-checking macro for the built-in Python object
|
||||
types, to check for exact type match too (for example,
|
||||
PyDict_Check(x) is true if x is an instance of dictionary or of a
|
||||
dictionary subclass, while PyDict_CheckExact(x) is true only if x
|
||||
is a dictionary).
|
||||
|
||||
|
||||
Creating a subtype of a built-in type in C
|
||||
|
@ -427,7 +429,7 @@ Creating a subtype of a built-in type in C
|
|||
be the first member of the structure; any following members are
|
||||
additions. Also note that the base type is not referenced via a
|
||||
pointer; the actual contents of its structure must be included!
|
||||
(The goal is for the memory lay out of the beginning of the
|
||||
(The goal is for the memory layout of the beginning of the
|
||||
subtype instance to be the same as that of the base type
|
||||
instance.)
|
||||
|
||||
|
@ -553,8 +555,8 @@ Subtyping in Python
|
|||
|
||||
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.
|
||||
where dict is the dictionary resulting from execution of the
|
||||
class body. In other words, the metatype (M) is called.
|
||||
|
||||
Note that even though the example has only one base, we still pass
|
||||
in a (singleton) sequence of bases; this makes the interface
|
||||
|
@ -577,11 +579,11 @@ Subtyping in Python
|
|||
|
||||
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
|
||||
suite defines a variable __metaclass__, that is the metatype
|
||||
to call. (Note that setting __metaclass__ at the module level
|
||||
only affects class statements without a base class and without an
|
||||
explicit __metaclass__ declaration; but setting __metaclass__ in a
|
||||
class statement overrides the default metatype unconditionally.)
|
||||
class suite overrides the default metatype unconditionally.)
|
||||
|
||||
Second, with multiple bases, not all bases need to have the same
|
||||
metatype. This is called a metaclass conflict [1]. Some
|
||||
|
@ -590,8 +592,8 @@ Subtyping in Python
|
|||
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
|
||||
This conflict resolution can be implemented by the metatype
|
||||
constructors: the class statement just calls the metatype of the first
|
||||
base (or that specified by the __metaclass__ variable), and this
|
||||
metatype's constructor looks for the most derived metatype. If
|
||||
that is itself, it proceeds; otherwise, it calls that metatype's
|
||||
|
@ -621,17 +623,16 @@ Subtyping in Python
|
|||
turned out to be unnecessary.)
|
||||
|
||||
In any case, the work for creating C is done by M's tp_new() slot.
|
||||
It allocates space for an "extended" type structure, which
|
||||
contains space for: the type object; the auxiliary structures
|
||||
(as_sequence etc.); the string object containing the type name (to
|
||||
ensure that this object isn't deallocated while the type object is
|
||||
still referencing it); and some more auxiliary storage (to be
|
||||
described later). It initializes this storage to zeros except for
|
||||
a few crucial slots (for example, tp_name is set to point to the
|
||||
type name) and then sets the tp_base slot to point to B. Then
|
||||
PyType_InitDict() is called to inherit B's slots. Finally, C's
|
||||
tp_dict slot is updated with the contents of the namespace
|
||||
dictionary (the third argument to the call to M).
|
||||
It allocates space for an "extended" type structure, containing:
|
||||
the type object; the auxiliary structures (as_sequence etc.); the
|
||||
string object containing the type name (to ensure that this object
|
||||
isn't deallocated while the type object is still referencing it); and
|
||||
some auxiliary storage (to be described later). It initializes this
|
||||
storage to zeros except for a few crucial slots (for example, tp_name
|
||||
is set to point to the type name) and then sets the tp_base slot to
|
||||
point to B. Then PyType_InitDict() is called to inherit B's slots.
|
||||
Finally, C's tp_dict slot is updated with the contents of the
|
||||
namespace dictionary (the third argument to the call to M).
|
||||
|
||||
|
||||
Multiple inheritance
|
||||
|
@ -672,9 +673,9 @@ Multiple inheritance
|
|||
|
||||
(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
|
||||
the structure layout, we find that an A instance has the layout
|
||||
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
|
||||
has the same layout; since there are no structure member layout
|
||||
conflicts, this is okay.
|
||||
|
||||
Here's another example:
|
||||
|
@ -688,7 +689,7 @@ Multiple inheritance
|
|||
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
|
||||
layout 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
|
||||
|
@ -700,7 +701,7 @@ Multiple inheritance
|
|||
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.
|
||||
structure has the same layout 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:
|
||||
|
@ -722,7 +723,7 @@ Multiple inheritance
|
|||
below.
|
||||
|
||||
|
||||
Method resolution order (the lookup rule)
|
||||
MRO: Method resolution order (the lookup rule)
|
||||
|
||||
With multiple inheritance comes the question of method resolution
|
||||
order: the order in which a class or type and its bases are
|
||||
|
@ -776,7 +777,7 @@ Method resolution order (the lookup rule)
|
|||
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
|
||||
here is probably the reason why multiple inheritance is unpopular
|
||||
in classic Python.
|
||||
|
||||
Why will this be a problem in the new system? The 'object' type
|
||||
|
@ -917,7 +918,7 @@ XXX To be done
|
|||
|
||||
- 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
|
||||
or introduce a new name (__getattrhook__?) for the old
|
||||
semantics?
|
||||
|
||||
- whether __dynamic__ should be default
|
||||
|
@ -937,15 +938,9 @@ XXX To be done
|
|||
Implementation
|
||||
|
||||
A prototype implementation of this PEP (and for PEP 252) is
|
||||
available from CVS as a branch named "descr-branch". To
|
||||
experiment with this implementation, proceed to check out Python
|
||||
from CVS according to the instructions at
|
||||
http://sourceforge.net/cvs/?group_id=5470 but add the arguments
|
||||
"-r descr-branch" to the cvs checkout command. (You can also
|
||||
start with an existing checkout and do "cvs update -r
|
||||
descr-branch".) For some examples of the features described here,
|
||||
see the file Lib/test/test_descr.py and the extension module
|
||||
Modules/xxsubtype.c.
|
||||
available from CVS, and in the series of Python 2.2 alpha releases.
|
||||
For some examples of the features described here, see the file
|
||||
Lib/test/test_descr.py and the extension module Modules/xxsubtype.c.
|
||||
|
||||
|
||||
References
|
||||
|
|
Loading…
Reference in New Issue