PEP 520: Ordered Class Definition Namespace (round 3)
I've updated the PEP to reflect feedback up to this point. The reception has been positive. The only change to the original proposal has been that a manually set __definition_order__ must be a tuple of identifiers or None (rather that using the value as-is). All other updates to the PEP have been clarification. Guido, at this point I believe the PEP is ready for pronouncement. * I've included the most recent copy of the text below. Thanks. -eric ============================== PEP: 520 Title: Ordered Class Definition Namespace Version: $Revision$ Last-Modified: $Date$ Author: Eric Snow <ericsnowcurrently@gmail.com> Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 7-Jun-2016 Python-Version: 3.6 Post-History: 7-Jun-2016 Abstract ======== When a class is defined using a ``class`` statement, the class body is executed within a namespace. After the execution completes, that namespace is copied into new ``dict`` and the original definition namespace is discarded. The new copy is stored away as the class's namespace and is exposed as ``__dict__`` through a read-only proxy. This PEP changes the default class definition namespace to ``OrderedDict``. The long-lived class namespace (``__dict__``) will remain a ``dict``. Furthermore, the order in which the attributes are defined in each class body will now be preserved in the ``__definition_order__`` attribute of the class. This allows introspection of the original definition order, e.g. by class decorators. 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`` (or ``None``) * ``__definition_order__`` is a read-only attribute * ``__definition_order__`` is always set: 1. if ``__definition_order__`` is defined in the class body then it must be a ``tuple`` of identifiers or ``None``; any other value will result in ``TypeError`` 2. classes that do not have a class definition (e.g. builtins) have their ``__definition_order__`` set to ``None`` 3. classes 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 for the default behavior:: 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 not (k.startswith('__') and 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 feature focused on more than class definition. 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 nearly everyone they would only be clutter, causing the same extra work for everyone. Why None instead of an empty tuple? ----------------------------------- A key objective of adding ``__definition_order__`` is to preserve information in class definitions which was lost prior to this PEP. One consequence is that ``__definition_order__`` implies an original class definition. Using ``None`` allows us to clearly distinquish classes that do not have a definition order. An empty tuple clearly indicates a class that came from a definition statement but did not define any attributes there. Why None instead of not setting the attribute? ---------------------------------------------- The absence of an attribute requires more complex handling than ``None`` does for consumers of ``__definition_order__``. Why constrain manually set values? ---------------------------------- If ``__definition_order__`` is manually set in the class body then it will be used. We require it to be a tuple of identifiers (or ``None``) so that consumers of ``__definition_order__`` may have a consistent expectation for the value. That helps maximize the feature's usefulness. Why is __definition_order__ even necessary? ------------------------------------------- Since the definition order is not preserved in ``__dict__``, it is 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 ============ <class>.__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 then be used as the mapping proxied as ``__dict__``. Doing so would mostly provide the same semantics. However, using ``OrderedDict`` for ``__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.
From: Eric Snow Sent: Saturday, June 11, 2016 10:37 PM To: Python-Dev; Guido van Rossum Subject: [Python-Dev] PEP 520: Ordered Class Definition Namespace (round 3)
The only change to the original proposal has been that a manually set __definition_order__ must be a tuple of identifiers or None (rather that using the value as-is).
1. if ``__definition_order__`` is defined in the class body then it must be a ``tuple`` of identifiers or ``None``; any other value will result in ``TypeError``
Why not just any arbitrary iterable, which get converted to a tuple at runtime? __slots__ allows any arbitrary iterable:
def g(): ... yield "foo" ... yield "bar" ... yield "baz" class C: ... __slots__ = g() C.__slots__ <generator object g at 0x0074A9F0> C.__slots__.gi_running False dir(C) [<snip>, 'bar', 'baz', 'foo']
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.
Typo: missing leading underscores in __definition_order__
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.
Perhaps add a mention that isinstance(namespace, dict) will still be true, so users don't get unnecessarily confused.
<class>.__dict__ as OrderedDict -------------------------------
<class> looks weird to me. I tend to use `cls` (although `klass` isn't uncommon). `C` might also not be a bad choice. Thanks! -Emanuel
On Sat, Jun 11, 2016 at 7:51 PM, Émanuel Barry <vgr255@live.ca> wrote:
From: Eric Snow 1. if ``__definition_order__`` is defined in the class body then it must be a ``tuple`` of identifiers or ``None``; any other value will result in ``TypeError``
Why not just any arbitrary iterable, which get converted to a tuple at runtime?
An arbitrary iterable does not necessarily infer a definition order. For example, dict is an iterable but the order is undefined. Also, I'd rather favor simplicity for this (most likely) uncommon corner case of manually setting __definition_order__, particularly at the start. If it proves to be a problematic restriction in the future we can loosen it.
__slots__ allows any arbitrary iterable:
Yes, but __slots__ is not order-sensitive.
is already complete by the time ``definition_order__`` is set, the
Typo: missing leading underscores in __definition_order__
I'll fix that.
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.
Perhaps add a mention that isinstance(namespace, dict) will still be true, so users don't get unnecessarily confused.
Good point.
<class>.__dict__ as OrderedDict -------------------------------
<class> looks weird to me. I tend to use `cls` (although `klass` isn't uncommon). `C` might also not be a bad choice.
Yes, that is better. -eric
From: Eric Snow Sent: Saturday, June 11, 2016 11:02 PM To: Émanuel Barry Cc: Python-Dev Subject: Re: [Python-Dev] PEP 520: Ordered Class Definition Namespace (round 3)
On Sat, Jun 11, 2016 at 7:51 PM, Émanuel Barry <vgr255@live.ca> wrote:
From: Eric Snow 1. if ``__definition_order__`` is defined in the class body then it must be a ``tuple`` of identifiers or ``None``; any other value will result in ``TypeError``
Why not just any arbitrary iterable, which get converted to a tuple at runtime?
An arbitrary iterable does not necessarily infer a definition order. For example, dict is an iterable but the order is undefined. Also, I'd rather favor simplicity for this (most likely) uncommon corner case of manually setting __definition_order__, particularly at the start. If it proves to be a problematic restriction in the future we can loosen it.
Point. This can always be revised later (I'm probably overthinking this as always ;)
-eric
-Emanuel
On Sun, Jun 12, 2016 at 5:37 AM, Eric Snow <ericsnowcurrently@gmail.com> wrote:
The following code demonstrates roughly equivalent semantics for the default behavior::
class Meta(type): def __prepare__(cls, *args, **kwargs):
Shouldn't this be wrapped with a classmethod decorator? +1 from me. --Berker
participants (3)
-
Berker Peksağ
-
Eric Snow
-
Émanuel Barry