[Python-Dev] PEP 520: Ordered Class Definition Namespace (round 3) (original) (raw)
Eric Snow ericsnowcurrently at gmail.com
Sat Jun 11 22:37:17 EDT 2016
- Previous message (by thread): [Python-Dev] Current Python 3.2 status?
- Next message (by thread): [Python-Dev] PEP 520: Ordered Class Definition Namespace (round 3)
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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: RevisionRevisionRevision Last-Modified: DateDateDate Author: Eric Snow <ericsnowcurrently at 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 atuple
(orNone
)__definition_order__
is a read-only attribute__definition_order__
is always set:- if
__definition_order__
is defined in the class body then it must be atuple
of identifiers orNone
; any other value will result inTypeError
- classes that do not have a class definition (e.g. builtins) have
their
__definition_order__
set toNone
- classes for which `__prepare__()
returned something other than
OrderedDict(or a subclass) have their
__definition_order__set to
None`` (except where #1 applies)
- if
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
.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.
- Previous message (by thread): [Python-Dev] Current Python 3.2 status?
- Next message (by thread): [Python-Dev] PEP 520: Ordered Class Definition Namespace (round 3)
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]