python-peps/pep-0549.rst

119 lines
3.9 KiB
ReStructuredText
Raw Normal View History

PEP: 549
2017-09-05 14:01:39 -04:00
Title: Instance Properties
Version: $Revision$
Last-Modified: $Date$
Author: larry@hastings.org (Larry Hastings)
Status: Draft
Type: Standards Track
Created: 4-Sep-2017
Python-Version: 3.7
Post-History: 4-Sep-2017
Abstract
--------
Python's descriptor protocol requires that descriptors
be members of the *type* of an object. This PEP proposes
an extension to the descriptor protocol allowing use of
the descriptor protocol for members of *instances.* This
would permit using properties in modules.
Rationale
---------
Python's descriptor protocol guides programmers towards
elegant API design. If your class supports a data-like
member, and you *might* someday need to run code when
changing the member's value, you're encouraged to
simply declare it as a simple data member of the class
for now. If in the future you do need to run code, you
can change it to a "property", and happily the API doesn't
change.
Unfortunately this doesn't work with modules. Modules are
instances of a single generic ``module`` type, and it's not
feasible to modify or subclass this type to add a property
to one's module. This means that programmers facing this
API design decision, where the data-like member is a singleton
stored in a module, must preemptively add ugly "getters"
and "setters" for the data.
Adding support for module properties in pure Python is possible
only by using hacks like:
* peeking in ``sys.getframe()``,
* changing the type of an object after it's created, or
* replacing the object stored in ``sys.modules``.
These techniques can be made to work, but they're fragile.
This PEP proposes a per-type opt-in extension to the descriptor
protocol specifically designed to enable properties in modules.
The mechanism is a way to honor the descriptor protocol for
members of *instances* of a class without the member being declared
as a class variable.
Although this is being proposed as a general mechanism, the author
currently only forsees this as being useful for module objects.
Implementation
--------------
The basic idea is simple: modify the ``tp_descr_get`` and ``tp_descr_set``
functions exposed by ``PyModule_Type`` to inspect the attribute interacted
with, and if it supports the descriptor protocol, call the relevant
exposed function.
Our implementation faces two challenges:
1. Since this code will be run every time an attribute is looked up on a
method, it needs to add very little overhead in the general case,
where the object stored in the attribute is not a descriptor.
2. Since functions are descriptors, we must take care to *not* honor
the descriptor protocol for all objects. Otherwise, all module-level
functions will suddenly become bound to the module instance as if
they were method calls on the module object. The module handle would
be passed in as a "self" argument to all module-level functions.
Both challenges can be solved with the same approach: we define a new
"fast subclass" flag that means "This object is a desciptor, and it
should be honored directly when this object is looked up as an
attribute of an instance". So far the only placed where this flag
is set is on ``PyProperty_Type``.
Prototype
---------
A prototype of this functionality is under development
at GitHub [github]_.
Acknowledgements
----------------
Armin Rigo essentially proposed this mechanism when presented
with the idea of "module properties", and educated the author
both on the complexities of the problem and the proper solution.
References
----------
.. [github]
https://github.com/larryhastings/cpython/tree/module-properties
Copyright
---------
This document has been placed in the public domain.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
fill-column: 70
End: