How does a descriptor with __set__ but without __get__ work?
<p>I read somewhere about the fact you can have a descriptor with <code>__set__</code> and without <code>__get__</code>.</p>

<p>How does it work? </p>

<p>Does it count as a data descriptor? Is it a non-data descriptor?</p>

<p>Here is a code example:</p>

<pre><code>class Desc:
    def __init__(self, name):
        self.name = name
    def __set__(self, inst, value):
        inst.__dict__[self.name] = value
        print("set", self.name)

class Test:
    attr = Desc("attr")

&gt;&gt;&gt;myinst = Test()
&gt;&gt;&gt; myinst.attr = 1234
set attr
&gt;&gt;&gt; myinst.attr
1234
&gt;&gt;&gt; myinst.attr = 5678
set attr
&gt;&gt;&gt; myinst.attr
5678
</code></pre>

<p>The descriptor you've given in the example is a data descriptor.</p>

<p>Upon setting the attribute, as any other data descriptor, it takes the highest priority and is called like so:</p>

<pre><code>type(myinst).__dict__["attr"].__set__(myinst, 1234)
</code></pre>

<p>This in turn, adds <code>attr</code> to the instance dictionary according to your <code>__set__</code> method.</p>

<p>Upon attribute access, the descriptor is checked for having the <code>__get__</code> method but fails, causing for the search to be redirected to the instance's dictionary like so:</p>

<pre><code>myinst.__dict__["attr"]
</code></pre>

<p>If it is not found in the instance dictionary, the descriptor itself is returned.</p>

<p>This behavior is shortly documented in the <a href="https://docs.python.org/3.6/reference/datamodel.html#invoking-descriptors" rel="nofollow">data model</a> like so:</p>

<blockquote>
  <p>If it does not define <code>__get__()</code>, then accessing the attribute will
  return the descriptor object itself unless there is a value in the
  objectâs instance dictionary.</p>
</blockquote>

<p>Common usecases include avoiding <code>{instance: value}</code> dictionaries inside the descriptors, and caching values in an efficient way.</p>

<hr>

<p>In Python 3.6, <code>__set_name__</code> was added to the descriptor protocol thus eliminating the need for specifying the name inside the descriptor. This way, your descriptor can be written like so:</p>

<pre><code>class Desc:
    def __set_name__(self, owner, name):
        self.name = name
    def __set__(self, inst, value):
        inst.__dict__[self.name] = value
        print("set", self.name)

class Test:
    attr = Desc()
</code></pre>

python python-descriptors