[Python-Dev] PEP: Ordered Class Definition Namespace

Eric Snow ericsnowcurrently at gmail.com
Tue Jun 7 13:51:52 EDT 2016


Hi all,

Following discussion a few years back (and rough approval from Guido
[1]), I started work on using OrderedDict for the class definition
namespace by default.  The bulk of the effort lay in implementing
OrderedDict in C, which I got landed just in time for 3.5.  The
remaining work was quite minimal and the actual change is quite small.

My intention was to land the patch soon, having gone through code
review during PyCon.  However, Nick pointed out to me the benefit of
having a concrete point of reference for the change, as well as making
sure it isn't a problem for other implementations.  So in that spirit,
here's a PEP for the change.  Feedback is welcome, particularly from
from other implementors.

-eric

[1] https://mail.python.org/pipermail/python-ideas/2013-February/019704.html

==================================================

PEP: XXX
Title: Ordered Class Definition Namespace
Version: $Revision$
Last-Modified: $Date$
Author: Eric Snow <ericsnowcurrently at gmail.com>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 4-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 ``type.__dict__``
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:

  * if ``__definition_order__`` is defined in the class body then it
    is used
  * types that do not have a class definition (e.g. builtins) have
    their ``__definition_order__`` set to ``None``
  * types for which `__prepare__()`` returned something other than
    ``OrderedDict`` (or a subclass) have their ``__definition_order__``
    set to ``None``

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.


Compatibility
=============

This PEP does not break backward compatibility, except in the case that
someone relies *strictly* on dicts 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)

.. [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.


More information about the Python-Dev mailing list