Furhter updates
This commit is contained in:
parent
2042d9e44a
commit
3a6f06fe6e
101
pep-0447.txt
101
pep-0447.txt
|
@ -56,7 +56,8 @@ Rationale
|
||||||
It is currently not possible to influence how the `super class`_ looks
|
It is currently not possible to influence how the `super class`_ looks
|
||||||
up attributes (that is, ``super.__getattribute__`` unconditionally
|
up attributes (that is, ``super.__getattribute__`` unconditionally
|
||||||
peeks in the class ``__dict__``), and that can be problematic for
|
peeks in the class ``__dict__``), and that can be problematic for
|
||||||
dynamic classes that can grow new methods on demand.
|
dynamic classes that can grow new methods on demand, for example dynamic
|
||||||
|
proxy classes.
|
||||||
|
|
||||||
The ``__getdescriptor__`` method makes it possible to dynamically add
|
The ``__getdescriptor__`` method makes it possible to dynamically add
|
||||||
attributes even when looking them up using the `super class`_.
|
attributes even when looking them up using the `super class`_.
|
||||||
|
@ -113,7 +114,8 @@ The attribute resolution proces as implemented by ``object.__getattribute__``
|
||||||
straightforward, but not entirely so without reading C code.
|
straightforward, but not entirely so without reading C code.
|
||||||
|
|
||||||
The current CPython implementation of object.__getattribute__ is basicly
|
The current CPython implementation of object.__getattribute__ is basicly
|
||||||
equivalent to the following (pseudo-) Python code (excluding some house keeping and speed tricks)::
|
equivalent to the following (pseudo-) Python code (excluding some house
|
||||||
|
keeping and speed tricks)::
|
||||||
|
|
||||||
|
|
||||||
def _PyType_Lookup(tp, name):
|
def _PyType_Lookup(tp, name):
|
||||||
|
@ -269,6 +271,9 @@ this.
|
||||||
In C code
|
In C code
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
A new type flag ``Py_TPFLAGS_GETDESCRIPTOR`` with value ``(1UL << 11)`` that
|
||||||
|
indicates that the new slot is present and to be used.
|
||||||
|
|
||||||
A new slot ``tp_getdescriptor`` is added to the ``PyTypeObject`` struct, this
|
A new slot ``tp_getdescriptor`` is added to the ``PyTypeObject`` struct, this
|
||||||
slot corresponds to the ``__getdescriptor__`` method on `type`_.
|
slot corresponds to the ``__getdescriptor__`` method on `type`_.
|
||||||
|
|
||||||
|
@ -281,6 +286,9 @@ superclasses, and should not invoke descriptors. The method returns ``NULL``
|
||||||
without setting an exception when the *name* cannot be found, and returns a
|
without setting an exception when the *name* cannot be found, and returns a
|
||||||
new reference otherwise (not a borrowed reference).
|
new reference otherwise (not a borrowed reference).
|
||||||
|
|
||||||
|
Classes with a ``tp_getdescriptor`` slot must add ``Py_TPFLAGS_GETDESCRIPTOR``
|
||||||
|
to ``tp_flags`` to indicate that new slot must be used.
|
||||||
|
|
||||||
Use of this hook by the interpreter
|
Use of this hook by the interpreter
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
@ -300,12 +308,16 @@ Because of this ``_PyType_Lookup`` will be renamed to ``_PyType_LookupName``,
|
||||||
this will cause compile-time errors for all out-of-tree users of this
|
this will cause compile-time errors for all out-of-tree users of this
|
||||||
private API.
|
private API.
|
||||||
|
|
||||||
|
For the same reason ``_PyType_LookupId`` is renamed to ``_PyType_LookupId2``.
|
||||||
|
A number of other functions in typeobject.c with the same issue do not get
|
||||||
|
an updated name because they are private to that file.
|
||||||
|
|
||||||
The attribute lookup cache in ``Objects/typeobject.c`` is disabled for classes
|
The attribute lookup cache in ``Objects/typeobject.c`` is disabled for classes
|
||||||
that have a metaclass that overrides ``__getdescriptor__``, because using the
|
that have a metaclass that overrides ``__getdescriptor__``, because using the
|
||||||
cache might not be valid for such classes.
|
cache might not be valid for such classes.
|
||||||
|
|
||||||
Impact of this PEP on introspection
|
Impact of this PEP on introspection
|
||||||
-----------------------------------
|
===================================
|
||||||
|
|
||||||
Use of the method introduced in this PEP can affect introspection of classes
|
Use of the method introduced in this PEP can affect introspection of classes
|
||||||
with a metaclass that uses a custom ``__getdescriptor__`` method. This section
|
with a metaclass that uses a custom ``__getdescriptor__`` method. This section
|
||||||
|
@ -334,33 +346,46 @@ changes to the visible behaviour of the ``object.__getattribute__``.
|
||||||
be ignored and is another way in which the result of ``inspect.getattr_static``
|
be ignored and is another way in which the result of ``inspect.getattr_static``
|
||||||
can be different from that of ``builtin.getattr``.
|
can be different from that of ``builtin.getattr``.
|
||||||
|
|
||||||
* ``inspect.getmembers`` and ``inspect.get_class_attrs``
|
* ``inspect.getmembers`` and ``inspect.classify_class_attrs``
|
||||||
|
|
||||||
Both of these functions directly access the class __dict__ of classes along
|
Both of these functions directly access the class __dict__ of classes along
|
||||||
the MRO, and hence can be affected by a custom ``__getdescriptor__`` method.
|
the MRO, and hence can be affected by a custom ``__getdescriptor__`` method.
|
||||||
|
|
||||||
**TODO**: I haven't fully worked out what the impact of this is, and if there
|
Code with a custom ``__getdescriptor__`` method that want to play nice with
|
||||||
are mitigations for those using either updates to these functions, or
|
these methods also needs to ensure that the ``__dict__`` is set up correctly
|
||||||
additional methods that users should implement to be fully compatible with
|
when that is accessed directly by Python code.
|
||||||
these functions.
|
|
||||||
|
|
||||||
One possible mitigation is to have a custom ``__getattribute__`` for these
|
Note that ``inspect.getmembers`` is used by ``pydoc`` and hence this can
|
||||||
classes that fills ``__dict__`` before returning and and defers to the
|
affect runtime documentation introspection.
|
||||||
default implementation for other attributes.
|
|
||||||
|
|
||||||
* Direct introspection of the class ``__dict__``
|
* Direct introspection of the class ``__dict__``
|
||||||
|
|
||||||
Any code that directly access the class ``__dict__`` for introspection
|
Any code that directly access the class ``__dict__`` for introspection
|
||||||
can be affected by a custom ``__getdescriptor__`` method.
|
can be affected by a custom ``__getdescriptor__`` method, see the previous
|
||||||
|
item.
|
||||||
|
|
||||||
|
|
||||||
Performance impact
|
Performance impact
|
||||||
------------------
|
==================
|
||||||
|
|
||||||
**WARNING**: The benchmark results in this section are old, and will be updated
|
**WARNING**: The benchmark results in this section are old, and will be updated
|
||||||
when I've ported the patch to the current trunk. I don't expect significant
|
when I've ported the patch to the current trunk. I don't expect significant
|
||||||
changes to the results in this section.
|
changes to the results in this section.
|
||||||
|
|
||||||
|
Micro benchmarks
|
||||||
|
----------------
|
||||||
|
|
||||||
|
`Issue 18181`_ has a micro benchmark as one of its attachments
|
||||||
|
(`pep447-micro-bench.py`_) that specifically tests the speed of attribute
|
||||||
|
lookup, both directly and through super.
|
||||||
|
|
||||||
|
Note that attribute lookup with deep class hierarchies is significantly slower
|
||||||
|
when using a custom ``__getdescriptor__`` method. This is because the
|
||||||
|
attribute lookup cache for CPython cannot be used when having this method.
|
||||||
|
|
||||||
|
Pybench
|
||||||
|
-------
|
||||||
|
|
||||||
The pybench output below compares an implementation of this PEP with the
|
The pybench output below compares an implementation of this PEP with the
|
||||||
regular source tree, both based on changeset a5681f50bae2, run on an idle
|
regular source tree, both based on changeset a5681f50bae2, run on an idle
|
||||||
machine an Core i7 processor running Centos 6.4.
|
machine an Core i7 processor running Centos 6.4.
|
||||||
|
@ -579,10 +604,10 @@ the performance impact is minimal::
|
||||||
|
|
||||||
|
|
||||||
Alternative proposals
|
Alternative proposals
|
||||||
---------------------
|
=====================
|
||||||
|
|
||||||
``__getattribute_super__``
|
``__getattribute_super__``
|
||||||
..........................
|
--------------------------
|
||||||
|
|
||||||
An earlier version of this PEP used the following static method on classes::
|
An earlier version of this PEP used the following static method on classes::
|
||||||
|
|
||||||
|
@ -593,7 +618,7 @@ necessarily limited to working only with ``super.__getattribute__``.
|
||||||
|
|
||||||
|
|
||||||
Reuse ``tp_getattro``
|
Reuse ``tp_getattro``
|
||||||
.....................
|
---------------------
|
||||||
|
|
||||||
It would be nice to avoid adding a new slot, thus keeping the API simpler and
|
It would be nice to avoid adding a new slot, thus keeping the API simpler and
|
||||||
easier to understand. A comment on `Issue 18181`_ asked about reusing the
|
easier to understand. A comment on `Issue 18181`_ asked about reusing the
|
||||||
|
@ -605,16 +630,44 @@ That won't work because ``tp_getattro`` will look in the instance
|
||||||
This would mean that using ``tp_getattro`` instead of peeking the class
|
This would mean that using ``tp_getattro`` instead of peeking the class
|
||||||
dictionaries changes the semantics of the `super class`_.
|
dictionaries changes the semantics of the `super class`_.
|
||||||
|
|
||||||
Alternate placement of the new method
|
Alternative placement of the new method
|
||||||
.....................................
|
---------------------------------------
|
||||||
|
|
||||||
This PEP proposes to add ``__getdescriptor__`` as a method on the metaclass.
|
This PEP proposes to add ``__getdescriptor__`` as a method on the metaclass.
|
||||||
An alternative would be to add it as a class method on the class itself
|
An alternative would be to add it as a class method on the class itself
|
||||||
(simular to how ``__new__`` is a `staticmethod`_ of the class and not a method
|
(simular to how ``__new__`` is a `staticmethod`_ of the class and not a method
|
||||||
of the metaclass).
|
of the metaclass).
|
||||||
|
|
||||||
The two are functionally equivalent, and there's something to be said about
|
The advantage of using a method on the metaclass is that will give an error
|
||||||
not requiring the use of a meta class.
|
when two classes on the MRO have different metaclasses that may have different
|
||||||
|
behaviors for ``__getdescriptor__``. With a normal classmethod that problem
|
||||||
|
would pass undetected while it might cause subtle errors when running the code.
|
||||||
|
|
||||||
|
History
|
||||||
|
=======
|
||||||
|
|
||||||
|
* 23-Jul-2015: Added type flag ``Py_TPFLAGS_GETDESCRIPTOR`` after talking
|
||||||
|
with Guido.
|
||||||
|
|
||||||
|
The new flag is primarily useful to avoid crashing when loading an extension
|
||||||
|
for an older version of CPython and could have positive speed implications
|
||||||
|
as well.
|
||||||
|
|
||||||
|
* Jul-2014: renamed slot to ``__getdescriptor__``, the old name didn't
|
||||||
|
match the naming style of other slots and was less descriptive.
|
||||||
|
|
||||||
|
Discussion threads
|
||||||
|
==================
|
||||||
|
|
||||||
|
* The initial version of the PEP was send with
|
||||||
|
Message-ID `<75030FAC-6918-4E94-95DA-67A88D53E6F5@mac.com>`_
|
||||||
|
|
||||||
|
* Further discusion starting at a message with
|
||||||
|
Message-ID `<5BB87CC4-F31B-4213-AAAC-0C0CE738460C@mac.com>`_
|
||||||
|
|
||||||
|
* And more discussion starting at message with
|
||||||
|
Message-ID `<00AA7433-C853-4101-9718-060468EBAC54@mac.com>`_
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
References
|
References
|
||||||
|
@ -627,6 +680,12 @@ Copyright
|
||||||
|
|
||||||
This document has been placed in the public domain.
|
This document has been placed in the public domain.
|
||||||
|
|
||||||
|
.. _`<75030FAC-6918-4E94-95DA-67A88D53E6F5@mac.com>`: http://marc.info/?l=python-dev&m=137510220928964&w=2
|
||||||
|
|
||||||
|
.. _`<5BB87CC4-F31B-4213-AAAC-0C0CE738460C@mac.com>`: https://mail.python.org/pipermail/python-ideas/2014-July/028420.html
|
||||||
|
|
||||||
|
.. _`<00AA7433-C853-4101-9718-060468EBAC54@mac.com>`: https://mail.python.org/pipermail/python-dev/2013-July/127321.html
|
||||||
|
|
||||||
.. _`Issue 18181`: http://bugs.python.org/issue18181
|
.. _`Issue 18181`: http://bugs.python.org/issue18181
|
||||||
|
|
||||||
.. _`super class`: http://docs.python.org/3/library/functions.html#super
|
.. _`super class`: http://docs.python.org/3/library/functions.html#super
|
||||||
|
@ -652,3 +711,5 @@ This document has been placed in the public domain.
|
||||||
.. _`PyObjC`: http://pyobjc.sourceforge.net/
|
.. _`PyObjC`: http://pyobjc.sourceforge.net/
|
||||||
|
|
||||||
.. _`classmethod`: http://docs.python.org/3/library/functions.html#classmethod
|
.. _`classmethod`: http://docs.python.org/3/library/functions.html#classmethod
|
||||||
|
|
||||||
|
.. _`pep447-micro-bench.py`: http://bugs.python.org/file40013/pep447-micro-bench.py
|
||||||
|
|
Loading…
Reference in New Issue