Updated the PEP in accordance with the latest discussions. Added additional rationales and use cases, and added a link to the sample implementation.

This commit is contained in:
Talin 2007-03-16 00:36:00 +00:00
parent 4b14e34f00
commit 4c5f90cb3e
1 changed files with 59 additions and 30 deletions

View File

@ -52,6 +52,15 @@ Rationale
the new system allows the ordering or other early artifacts of
construction to be preserved and examined.
There proposed metaclass mechanism also supports a number of other
interesting use cases beyond preserving the ordering of declarations.
One use case is to insert symbols into the namespace of the class
body which are only valid during class construction. An example of
this might be "field constructors", small functions that are used in
the creation of class members. Another interesting possibility is
supporting forward references, i.e. references to Python
symbols that are declared further down in the class body.
The other, weaker, rationale is purely cosmetic: The current method
for specifying a metaclass is by assignment to the special variable
__metaclass__, which is considered by some to be aesthetically less
@ -105,12 +114,14 @@ Invoking the Metaclass
calling it; If it is not present, then a regular dictionary is used,
as illustrated in the following Python snippet.
def prepare_class(name, *bases, metaclass=type, **kwargs):
prepare = getattr(metaclass, '__prepare__', None)
if prepare is not None:
return prepare(name, bases, **kwargs)
else:
return dict()
def prepare_class(name, *bases, metaclass=None, **kwargs):
if metaclass is None:
metaclass = compute_default_metaclass(bases)
prepare = getattr(metaclass, '__prepare__', None)
if prepare is not None:
return prepare(name, bases, **kwargs)
else:
return dict()
The example above illustrates how the arguments to 'class' are
interpreted. The class name is the first argument, followed by
@ -127,13 +138,22 @@ Invoking the Metaclass
(just like it is now), except that the local variables dictionary
is replaced by the dictionary returned from __prepare__. This
dictionary object can be a regular dictionary or a custom mapping
type. It does not need to implement the full dictionary interface;
only the ability to insert items and retrieve them are
required. (Note: double check that this is true).
type.
Note that __prepare__ is generally a class method, not an instance
method because it is called before the metaclass instance (i.e. the
class itself) is created.
This dictionary-like object is not required to support the full
dictionary interface. A dictionary which supports a limited set of
dictionary operations will restrict what kinds of actions can occur
during evaluation of the class body. A minimal implementation might
only support adding and retrieving values from the dictionary - most
class bodies will do no more than that during evaluation. For some
classes, it may be desirable to support deletion as well. Many
metaclasses will need to make a copy of this dictionary afterwards,
so iteration or other means for reading out the dictionary contents
may also be useful.
The __prepare__ method will most often be implemented as a class
method rather than an instance method because it is called before
the metaclass instance (i.e. the class itself) is created.
Once the class body has finished evaluating, the metaclass will be
called (as a callable) with the class dictionary, which is no
@ -165,27 +185,27 @@ Example:
the names of all class members, in the order that they were
declared:
# The custom dictionary
class member_table(dict):
def __init__(self):
self.member_names = []
def __setitem__(self, key, value):
# if the key is not already defined, add to the
# list of keys.
if key not in self:
self.member_names.append(key)
# Call superclass
dict.setitem(self, key, value)
# The metaclass
class OrderedClass(type):
# The custom dictionary
class member_table(dict):
def __init__(self):
self.member_names = []
def __setitem__(self, key, value):
# if the key is not already defined, add to the
# list of keys.
if key not in self:
self.member_names.append(key)
# Call superclass
dict.setitem(self, key, value)
# The prepare function
@classmethod
def __prepare__(metacls, name, bases): # No keywords in this case
return metacls.member_table()
def __prepare__(metacls, name, bases): # No keywords in this case
return member_table()
# The metaclass invocation
def __init__(self, name, bases, classdict):
@ -206,6 +226,13 @@ Example:
def method2(self):
pass
Sample Implementation:
Guido van Rossum has created a patch which implements the new
functionality:
http://python.org/sf/1681101
Alternate Proposals
Josiah Carlson proposed using the name 'type' instead of
@ -232,10 +259,12 @@ Alternate Proposals
classes, and skip the whole 'custom dict' mechanism. This was based
on the observation that most use cases for a custom dict were for
the purposes of preserving order information. However, this idea has
two drawbacks, first because it means that an ordered dict
several drawbacks, first because it means that an ordered dict
implementation would have to be added to the set of built-in types
in Python, and second because it would impose a slight speed (and
complexity) penalty on all class declarations.
complexity) penalty on all class declarations. Later, several people
came up with ideas for use cases for custom dictionaries other
than preserving field orderings, so this idea was dropped.
Backwards Compatibility