From 9a5e309b5da79d9749369a63911b1db2ae042b9d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 7 Jun 2016 17:44:13 -0700 Subject: [PATCH] Add PEP 520: Ordered Class Definition Namespace --- pep-0520.txt | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 pep-0520.txt diff --git a/pep-0520.txt b/pep-0520.txt new file mode 100644 index 000000000..14cf48f88 --- /dev/null +++ b/pep-0520.txt @@ -0,0 +1,219 @@ +PEP: 520 +Title: Ordered Class Definition Namespace +Version: $Revision$ +Last-Modified: $Date$ +Author: Eric Snow +Status: Draft +Type: Standards Track +Content-Type: text/x-rst +Created: 7-Jun-2016 +Python-Version: 3.6 +Post-History: 7-Jun-2016 + + +Abstract +======== + +This PEP changes the default class definition namespace to ``OrderedDict``. +Furthermore, the order in which the attributes are defined in each class +body will now be preserved in ``type.__definition_order__``. This allows +introspection of the original definition order, e.g. by class decorators. + +Note: just to be clear, this PEP is *not* about changing ``__dict__`` for +classes to ``OrderedDict``. + + +Motivation +========== + +Currently the namespace used during execution of a class body defaults +to ``dict``. If the metaclass defines ``__prepare__()`` then the result +of calling it is used. Thus, before this PEP, if you needed your class +definition namespace to be ``OrderedDict`` you had to use a metaclass. + +Metaclasses introduce an extra level of complexity to code and in some +cases (e.g. conflicts) are a problem. So reducing the need for them is +worth doing when the opportunity presents itself. Given that we now have +a C implementation of ``OrderedDict`` and that ``OrderedDict`` is the +common use case for ``__prepare__()``, we have such an opportunity by +defaulting to ``OrderedDict``. + +The usefulness of ``OrderedDict``-by-default is greatly increased if the +definition order is directly introspectable on classes afterward, +particularly by code that is independent of the original class definition. +One of the original motivating use cases for this PEP is generic class +decorators that make use of the definition order. + +Changing the default class definition namespace has been discussed a +number of times, including on the mailing lists and in PEP 422 and +PEP 487 (see the References section below). + + +Specification +============= + +* the default class *definition* namespace is now ``OrderdDict`` +* the order in which class attributes are defined is preserved in the + new ``__definition_order__`` attribute on each class +* "dunder" attributes (e.g. ``__init__``, ``__module__``) are ignored +* ``__definition_order__`` is a tuple +* ``__definition_order__`` is a read-only attribute +* ``__definition_order__`` is always set: + + 1. if ``__definition_order__`` is defined in the class body then the + value is used as-is, though the attribute will still be read-only + 2. types that do not have a class definition (e.g. builtins) have + their ``__definition_order__`` set to ``None`` + 3. types for which `__prepare__()`` returned something other than + ``OrderedDict`` (or a subclass) have their ``__definition_order__`` + set to ``None`` (except where #1 applies) + +The following code demonstrates roughly equivalent semantics:: + + class Meta(type): + def __prepare__(cls, *args, **kwargs): + return OrderedDict() + + class Spam(metaclass=Meta): + ham = None + eggs = 5 + __definition_order__ = tuple(k for k in locals() + if (!k.startswith('__') or + !k.endswith('__'))) + +Note that [pep487_] proposes a similar solution, albeit as part of a +broader proposal. + +Why a tuple? +------------ + +Use of a tuple reflects the fact that we are exposing the order in +which attributes on the class were *defined*. Since the definition +is already complete by the time ``definition_order__`` is set, the +content and order of the value won't be changing. Thus we use a type +that communicates that state of immutability. + +Why a read-only attribute? +-------------------------- + +As with the use of tuple, making ``__definition_order__`` a read-only +attribute communicates the fact that the information it represents is +complete. Since it represents the state of a particular one-time event +(execution of the class definition body), allowing the value to be +replaced would reduce confidence that the attribute corresponds to the +original class body. + +If a use case for a writable (or mutable) ``__definition_order__`` +arises, the restriction may be loosened later. Presently this seems +unlikely and furthermore it is usually best to go immutable-by-default. + +Note that ``__definition_order__`` is centered on the class definition +body. The use cases for dealing with the class namespace (``__dict__``) +post-definition are a separate matter. ``__definition_order__`` would +be a significantly misleading name for a supporting feature. + +See [nick_concern_] for more discussion. + +Why ignore "dunder" names? +-------------------------- + +Names starting and ending with "__" are reserved for use by the +interpreter. In practice they should not be relevant to the users of +``__definition_order__``. Instead, for early everyone they would only +be clutter, causing the same extra work for everyone. + +Why is __definition_order__ even necessary? +------------------------------------------- + +Since the definition order is not preserved in ``__dict__``, it would be +lost once class definition execution completes. Classes *could* +explicitly set the attribute as the last thing in the body. However, +then independent decorators could only make use of classes that had done +so. Instead, ``__definition_order__`` preserves this one bit of info +from the class body so that it is universally available. + + +Compatibility +============= + +This PEP does not break backward compatibility, except in the case that +someone relies *strictly* on ``dict`` as the class definition namespace. +This shouldn't be a problem. + + +Changes +============= + +In addition to the class syntax, the following expose the new behavior: + +* builtins.__build_class__ +* types.prepare_class +* types.new_class + + +Other Python Implementations +============================ + +Pending feedback, the impact on Python implementations is expected to +be minimal. If a Python implementation cannot support switching to +`OrderedDict``-by-default then it can always set ``__definition_order__`` +to ``None``. + + +Implementation +============== + +The implementation is found in the tracker. [impl_] + + +Alternatives +============ + +type.__dict__ as OrderedDict +---------------------------- + +Instead of storing the definition order in ``__definition_order__``, +the now-ordered definition namespace could be copied into a new +``OrderedDict``. This would mostly provide the same semantics. + +However, using ``OrderedDict`` for ``type,__dict__`` would obscure the +relationship with the definition namespace, making it less useful. +Additionally, doing this would require significant changes to the +semantics of the concrete ``dict`` C-API. + +A "namespace" Keyword Arg for Class Definition +---------------------------------------------- + +PEP 422 introduced a new "namespace" keyword arg to class definitions +that effectively replaces the need to ``__prepare__()``. [pep422_] +However, the proposal was withdrawn in favor of the simpler PEP 487. + + +References +========== + +.. [impl] issue #24254 + (https://bugs.python.org/issue24254) + +.. [nick_concern] Nick's concerns about mutability + (https://mail.python.org/pipermail/python-dev/2016-June/144883.html) + +.. [pep422] PEP 422 + (https://www.python.org/dev/peps/pep-0422/#order-preserving-classes) + +.. [pep487] PEP 487 + (https://www.python.org/dev/peps/pep-0487/#defining-arbitrary-namespaces) + +.. [orig] original discussion + (https://mail.python.org/pipermail/python-ideas/2013-February/019690.html) + +.. [followup1] follow-up 1 + (https://mail.python.org/pipermail/python-dev/2013-June/127103.html) + +.. [followup2] follow-up 2 + (https://mail.python.org/pipermail/python-dev/2015-May/140137.html) + + +Copyright +=========== +This document has been placed in the public domain.