data descriptors do not override the class dictionary if __get__ is not defined

Adjust documentation and add a test to verify this behavior.

See http://mail.python.org/pipermail/python-dev/2010-January/095637.html for
discussion.
This commit is contained in:
Benjamin Peterson 2010-01-18 23:07:56 +00:00
parent c07336c673
commit 9179dab3f4
2 changed files with 30 additions and 11 deletions

View file

@ -1603,11 +1603,17 @@ Super Binding
``A.__dict__['m'].__get__(obj, A)``. ``A.__dict__['m'].__get__(obj, A)``.
For instance bindings, the precedence of descriptor invocation depends on the For instance bindings, the precedence of descriptor invocation depends on the
which descriptor methods are defined. Normally, data descriptors define both which descriptor methods are defined. A descriptor can define any combination
:meth:`__get__` and :meth:`__set__`, while non-data descriptors have just the of :meth:`__get__`, :meth:`__set__` and :meth:`__delete__`. If it does not
:meth:`__get__` method. Data descriptors always override a redefinition in an define :meth:`__get__`, then accessing the attribute will return the descriptor
object itself unless there is a value in the object's instance dictionary. If
the descriptor defines :meth:`__set__` and/or :meth:`__delete__`, it is a data
descriptor; if it defines neither, it is a non-data descriptor. Normally, data
descriptors define both :meth:`__get__` and :meth:`__set__`, while non-data
descriptors have just the :meth:`__get__` method. Data descriptors with
:meth:`__set__` and :meth:`__get__` defined always override a redefinition in an
instance dictionary. In contrast, non-data descriptors can be overridden by instance dictionary. In contrast, non-data descriptors can be overridden by
instances. [#]_ instances.
Python methods (including :func:`staticmethod` and :func:`classmethod`) are Python methods (including :func:`staticmethod` and :func:`classmethod`) are
implemented as non-data descriptors. Accordingly, instances can redefine and implemented as non-data descriptors. Accordingly, instances can redefine and
@ -2434,13 +2440,6 @@ object itself in order to be consistently invoked by the interpreter).
controlled conditions. It generally isn't a good idea though, since it can controlled conditions. It generally isn't a good idea though, since it can
lead to some very strange behaviour if it is handled incorrectly. lead to some very strange behaviour if it is handled incorrectly.
.. [#] A descriptor can define any combination of :meth:`__get__`,
:meth:`__set__` and :meth:`__delete__`. If it does not define :meth:`__get__`,
then accessing the attribute even on an instance will return the descriptor
object itself. If the descriptor defines :meth:`__set__` and/or
:meth:`__delete__`, it is a data descriptor; if it defines neither, it is a
non-data descriptor.
.. [#] For operands of the same type, it is assumed that if the non-reflected method .. [#] For operands of the same type, it is assumed that if the non-reflected method
(such as :meth:`__add__`) fails the operation is not supported, which is why the (such as :meth:`__add__`) fails the operation is not supported, which is why the
reflected method is not called. reflected method is not called.

View file

@ -4496,6 +4496,26 @@ order (MRO) for bases """
c[1:2] = 3 c[1:2] = 3
self.assertEqual(c.value, 3) self.assertEqual(c.value, 3)
def test_set_and_no_get(self):
# See
# http://mail.python.org/pipermail/python-dev/2010-January/095637.html
class Descr(object):
def __init__(self, name):
self.name = name
def __set__(self, obj, value):
obj.__dict__[self.name] = value
descr = Descr("a")
class X(object):
a = descr
x = X()
self.assertIs(x.a, descr)
x.a = 42
self.assertEqual(x.a, 42)
def test_getattr_hooks(self): def test_getattr_hooks(self):
# issue 4230 # issue 4230