diff --git a/pep-3115.txt b/pep-3115.txt index 83751131b..d7c086374 100644 --- a/pep-3115.txt +++ b/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