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:
parent
4b14e34f00
commit
4c5f90cb3e
89
pep-3115.txt
89
pep-3115.txt
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue