Descriptor protocol implementation of property()
<p>The Python <a href="https://docs.python.org/3.5/howto/descriptor.html#properties" rel="nofollow">descriptor How-To</a> describes how one could implement the <code>property()</code> in terms of descriptors. I do not understand the reason of the first if-block in the <code>__get__</code> method. Under what circumstances will <code>obj</code> be <code>None</code>? What is supposed to happen then? Why do the <code>__get__</code> and <code>__del__</code> methods not check for that?</p>

<p>Code is a bit lengthy, but it's probably better to give the full code rather than just a snippet. Questionable line is marked.</p>

<pre><code>class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        # =====&gt;&gt;&gt; What's the reason of this if block? &lt;&lt;&lt;=====
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)
</code></pre>

<p>From the <a href="https://docs.python.org/3.5/howto/descriptor.html#invoking-descriptors" rel="nofollow">descriptor documentation</a>:</p>

<blockquote>
  <p>The details of invocation depend on whether <code>obj</code> is an object or a class.</p>
</blockquote>

<p>Basically, instances call descriptors as <code>type(b).__dict__['x'].__get__(b, type(b))</code>, while classes call descriptors as <code>B.__dict__['x'].__get__(None, B)</code>. If <code>obj is None</code> means the getter was called from the class, not an instance.</p>

<p>This machinery is used for example to implement <a href="https://docs.python.org/3.5/howto/descriptor.html#static-methods-and-class-methods" rel="nofollow">classmethods</a>.</p>

<p>The <code>__set__</code> and <code>__delete__</code> do not check for <code>obj is None</code> because they can never be called like this. Only <code>__get__</code> is invoked when called from the class. Doing <code>cls.prop = 2</code> or <code>del cls.prop</code> will directly overwrite or delete the property object, without invoking <code>__set__</code> or <code>__delete__</code>.</p>

python