]> granicus.if.org Git - python/commitdiff
data descriptors do not override the class dictionary if __get__ is not defined
authorBenjamin Peterson <benjamin@python.org>
Mon, 18 Jan 2010 23:07:56 +0000 (23:07 +0000)
committerBenjamin Peterson <benjamin@python.org>
Mon, 18 Jan 2010 23:07:56 +0000 (23:07 +0000)
Adjust documentation and add a test to verify this behavior.

See http://mail.python.org/pipermail/python-dev/2010-January/095637.html for
discussion.

Doc/reference/datamodel.rst
Lib/test/test_descr.py

index 5189f1f2707d8e0fb6b5e60eed054f144f8a019e..0dc41878d4fc8a50fb6c0ec476fb14146f350c22 100644 (file)
@@ -1603,11 +1603,17 @@ Super Binding
    ``A.__dict__['m'].__get__(obj, A)``.
 
 For instance bindings, the precedence of descriptor invocation depends on the
-which descriptor methods are defined.  Normally, data descriptors define both
-:meth:`__get__` and :meth:`__set__`, while non-data descriptors have just the
-:meth:`__get__` method.  Data descriptors always override a redefinition in an
+which descriptor methods are defined.  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 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
-instances. [#]_
+instances.
 
 Python methods (including :func:`staticmethod` and :func:`classmethod`) are
 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
    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
    (such as :meth:`__add__`) fails the operation is not supported, which is why the
    reflected method is not called.
index 418337b57a22fd85681c8da745f18b0e3a29ed08..36af26cb6d5c8ad84fa22c486ed017f77fa719b4 100644 (file)
@@ -4496,6 +4496,26 @@ order (MRO) for bases """
         c[1:2] = 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):
         # issue 4230