Server : Apache System : Linux server1.cgrithy.com 3.10.0-1160.95.1.el7.x86_64 #1 SMP Mon Jul 24 13:59:37 UTC 2023 x86_64 User : nobody ( 99) PHP Version : 8.1.23 Disable Function : NONE Directory : /usr/share/doc/python-docs-2.7.5/html/howto/ |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Descriptor HowTo Guide — Python 2.7.5 documentation</title> <link rel="stylesheet" href="../_static/default.css" type="text/css" /> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> <script type="text/javascript"> var DOCUMENTATION_OPTIONS = { URL_ROOT: '../', VERSION: '2.7.5', COLLAPSE_INDEX: false, FILE_SUFFIX: '.html', HAS_SOURCE: true }; </script> <script type="text/javascript" src="../_static/jquery.js"></script> <script type="text/javascript" src="../_static/underscore.js"></script> <script type="text/javascript" src="../_static/doctools.js"></script> <script type="text/javascript" src="../_static/sidebar.js"></script> <link rel="search" type="application/opensearchdescription+xml" title="Search within Python 2.7.5 documentation" href="../_static/opensearch.xml"/> <link rel="author" title="About these documents" href="../about.html" /> <link rel="copyright" title="Copyright" href="../copyright.html" /> <link rel="top" title="Python 2.7.5 documentation" href="../index.html" /> <link rel="up" title="Python HOWTOs" href="index.html" /> <link rel="next" title="Idioms and Anti-Idioms in Python" href="doanddont.html" /> <link rel="prev" title="Curses Programming with Python" href="curses.html" /> <link rel="shortcut icon" type="image/png" href="../_static/py.png" /> <script type="text/javascript" src="../_static/copybutton.js"></script> </head> <body> <div class="related"> <h3>Navigation</h3> <ul> <li class="right" style="margin-right: 10px"> <a href="../genindex.html" title="General Index" accesskey="I">index</a></li> <li class="right" > <a href="../py-modindex.html" title="Python Module Index" >modules</a> |</li> <li class="right" > <a href="doanddont.html" title="Idioms and Anti-Idioms in Python" accesskey="N">next</a> |</li> <li class="right" > <a href="curses.html" title="Curses Programming with Python" accesskey="P">previous</a> |</li> <li><img src="../_static/py.png" alt="" style="vertical-align: middle; margin-top: -1px"/></li> <li><a href="http://www.python.org/">Python</a> »</li> <li> <a href="../index.html">Python 2.7.5 documentation</a> » </li> <li><a href="index.html" accesskey="U">Python HOWTOs</a> »</li> </ul> </div> <div class="document"> <div class="documentwrapper"> <div class="bodywrapper"> <div class="body"> <div class="section" id="descriptor-howto-guide"> <h1><a class="toc-backref" href="#id1">Descriptor HowTo Guide</a><a class="headerlink" href="#descriptor-howto-guide" title="Permalink to this headline">¶</a></h1> <table class="docutils field-list" frame="void" rules="none"> <col class="field-name" /> <col class="field-body" /> <tbody valign="top"> <tr class="field-odd field"><th class="field-name">Author:</th><td class="field-body">Raymond Hettinger</td> </tr> <tr class="field-even field"><th class="field-name">Contact:</th><td class="field-body"><python at rcn dot com></td> </tr> </tbody> </table> <div class="contents topic" id="contents"> <p class="topic-title first">Contents</p> <ul class="simple"> <li><a class="reference internal" href="#descriptor-howto-guide" id="id1">Descriptor HowTo Guide</a><ul> <li><a class="reference internal" href="#abstract" id="id2">Abstract</a></li> <li><a class="reference internal" href="#definition-and-introduction" id="id3">Definition and Introduction</a></li> <li><a class="reference internal" href="#descriptor-protocol" id="id4">Descriptor Protocol</a></li> <li><a class="reference internal" href="#invoking-descriptors" id="id5">Invoking Descriptors</a></li> <li><a class="reference internal" href="#descriptor-example" id="id6">Descriptor Example</a></li> <li><a class="reference internal" href="#properties" id="id7">Properties</a></li> <li><a class="reference internal" href="#functions-and-methods" id="id8">Functions and Methods</a></li> <li><a class="reference internal" href="#static-methods-and-class-methods" id="id9">Static Methods and Class Methods</a></li> </ul> </li> </ul> </div> <div class="section" id="abstract"> <h2><a class="toc-backref" href="#id2">Abstract</a><a class="headerlink" href="#abstract" title="Permalink to this headline">¶</a></h2> <p>Defines descriptors, summarizes the protocol, and shows how descriptors are called. Examines a custom descriptor and several built-in python descriptors including functions, properties, static methods, and class methods. Shows how each works by giving a pure Python equivalent and a sample application.</p> <p>Learning about descriptors not only provides access to a larger toolset, it creates a deeper understanding of how Python works and an appreciation for the elegance of its design.</p> </div> <div class="section" id="definition-and-introduction"> <h2><a class="toc-backref" href="#id3">Definition and Introduction</a><a class="headerlink" href="#definition-and-introduction" title="Permalink to this headline">¶</a></h2> <p>In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol. Those methods are <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a>, <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><tt class="xref py py-meth docutils literal"><span class="pre">__set__()</span></tt></a>, and <a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><tt class="xref py py-meth docutils literal"><span class="pre">__delete__()</span></tt></a>. If any of those methods are defined for an object, it is said to be a descriptor.</p> <p>The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, <tt class="docutils literal"><span class="pre">a.x</span></tt> has a lookup chain starting with <tt class="docutils literal"><span class="pre">a.__dict__['x']</span></tt>, then <tt class="docutils literal"><span class="pre">type(a).__dict__['x']</span></tt>, and continuing through the base classes of <tt class="docutils literal"><span class="pre">type(a)</span></tt> excluding metaclasses. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods were defined. Note that descriptors are only invoked for new style objects or classes (a class is new style if it inherits from <a class="reference internal" href="../library/functions.html#object" title="object"><tt class="xref py py-class docutils literal"><span class="pre">object</span></tt></a> or <a class="reference internal" href="../library/functions.html#type" title="type"><tt class="xref py py-class docutils literal"><span class="pre">type</span></tt></a>).</p> <p>Descriptors are a powerful, general purpose protocol. They are the mechanism behind properties, methods, static methods, class methods, and <a class="reference internal" href="../library/functions.html#super" title="super"><tt class="xref py py-func docutils literal"><span class="pre">super()</span></tt></a>. They are used throughout Python itself to implement the new style classes introduced in version 2.2. Descriptors simplify the underlying C-code and offer a flexible set of new tools for everyday Python programs.</p> </div> <div class="section" id="descriptor-protocol"> <h2><a class="toc-backref" href="#id4">Descriptor Protocol</a><a class="headerlink" href="#descriptor-protocol" title="Permalink to this headline">¶</a></h2> <p><tt class="docutils literal"><span class="pre">descr.__get__(self,</span> <span class="pre">obj,</span> <span class="pre">type=None)</span> <span class="pre">--></span> <span class="pre">value</span></tt></p> <p><tt class="docutils literal"><span class="pre">descr.__set__(self,</span> <span class="pre">obj,</span> <span class="pre">value)</span> <span class="pre">--></span> <span class="pre">None</span></tt></p> <p><tt class="docutils literal"><span class="pre">descr.__delete__(self,</span> <span class="pre">obj)</span> <span class="pre">--></span> <span class="pre">None</span></tt></p> <p>That is all there is to it. Define any of these methods and an object is considered a descriptor and can override default behavior upon being looked up as an attribute.</p> <p>If an object defines both <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a> and <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><tt class="xref py py-meth docutils literal"><span class="pre">__set__()</span></tt></a>, it is considered a data descriptor. Descriptors that only define <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a> are called non-data descriptors (they are typically used for methods but other uses are possible).</p> <p>Data and non-data descriptors differ in how overrides are calculated with respect to entries in an instance’s dictionary. If an instance’s dictionary has an entry with the same name as a data descriptor, the data descriptor takes precedence. If an instance’s dictionary has an entry with the same name as a non-data descriptor, the dictionary entry takes precedence.</p> <p>To make a read-only data descriptor, define both <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a> and <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><tt class="xref py py-meth docutils literal"><span class="pre">__set__()</span></tt></a> with the <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><tt class="xref py py-meth docutils literal"><span class="pre">__set__()</span></tt></a> raising an <a class="reference internal" href="../library/exceptions.html#exceptions.AttributeError" title="exceptions.AttributeError"><tt class="xref py py-exc docutils literal"><span class="pre">AttributeError</span></tt></a> when called. Defining the <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><tt class="xref py py-meth docutils literal"><span class="pre">__set__()</span></tt></a> method with an exception raising placeholder is enough to make it a data descriptor.</p> </div> <div class="section" id="invoking-descriptors"> <h2><a class="toc-backref" href="#id5">Invoking Descriptors</a><a class="headerlink" href="#invoking-descriptors" title="Permalink to this headline">¶</a></h2> <p>A descriptor can be called directly by its method name. For example, <tt class="docutils literal"><span class="pre">d.__get__(obj)</span></tt>.</p> <p>Alternatively, it is more common for a descriptor to be invoked automatically upon attribute access. For example, <tt class="docutils literal"><span class="pre">obj.d</span></tt> looks up <tt class="docutils literal"><span class="pre">d</span></tt> in the dictionary of <tt class="docutils literal"><span class="pre">obj</span></tt>. If <tt class="docutils literal"><span class="pre">d</span></tt> defines the method <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a>, then <tt class="docutils literal"><span class="pre">d.__get__(obj)</span></tt> is invoked according to the precedence rules listed below.</p> <p>The details of invocation depend on whether <tt class="docutils literal"><span class="pre">obj</span></tt> is an object or a class. Either way, descriptors only work for new style objects and classes. A class is new style if it is a subclass of <a class="reference internal" href="../library/functions.html#object" title="object"><tt class="xref py py-class docutils literal"><span class="pre">object</span></tt></a>.</p> <p>For objects, the machinery is in <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">object.__getattribute__()</span></tt></a> which transforms <tt class="docutils literal"><span class="pre">b.x</span></tt> into <tt class="docutils literal"><span class="pre">type(b).__dict__['x'].__get__(b,</span> <span class="pre">type(b))</span></tt>. The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to <a class="reference internal" href="../reference/datamodel.html#object.__getattr__" title="object.__getattr__"><tt class="xref py py-meth docutils literal"><span class="pre">__getattr__()</span></tt></a> if provided. The full C implementation can be found in <a class="reference internal" href="../c-api/object.html#PyObject_GenericGetAttr" title="PyObject_GenericGetAttr"><tt class="xref c c-func docutils literal"><span class="pre">PyObject_GenericGetAttr()</span></tt></a> in <a class="reference external" href="http://svn.python.org/view/python/trunk/Objects/object.c?view=markup">Objects/object.c</a>.</p> <p>For classes, the machinery is in <tt class="xref py py-meth docutils literal"><span class="pre">type.__getattribute__()</span></tt> which transforms <tt class="docutils literal"><span class="pre">B.x</span></tt> into <tt class="docutils literal"><span class="pre">B.__dict__['x'].__get__(None,</span> <span class="pre">B)</span></tt>. In pure Python, it looks like:</p> <div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">__getattribute__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span> <span class="s">"Emulate type_getattro() in Objects/typeobject.c"</span> <span class="n">v</span> <span class="o">=</span> <span class="nb">object</span><span class="o">.</span><span class="n">__getattribute__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="s">'__get__'</span><span class="p">):</span> <span class="k">return</span> <span class="n">v</span><span class="o">.</span><span class="n">__get__</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span> <span class="k">return</span> <span class="n">v</span> </pre></div> </div> <p>The important points to remember are:</p> <ul class="simple"> <li>descriptors are invoked by the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">__getattribute__()</span></tt></a> method</li> <li>overriding <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">__getattribute__()</span></tt></a> prevents automatic descriptor calls</li> <li><a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">__getattribute__()</span></tt></a> is only available with new style classes and objects</li> <li><a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">object.__getattribute__()</span></tt></a> and <tt class="xref py py-meth docutils literal"><span class="pre">type.__getattribute__()</span></tt> make different calls to <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a>.</li> <li>data descriptors always override instance dictionaries.</li> <li>non-data descriptors may be overridden by instance dictionaries.</li> </ul> <p>The object returned by <tt class="docutils literal"><span class="pre">super()</span></tt> also has a custom <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">__getattribute__()</span></tt></a> method for invoking descriptors. The call <tt class="docutils literal"><span class="pre">super(B,</span> <span class="pre">obj).m()</span></tt> searches <tt class="docutils literal"><span class="pre">obj.__class__.__mro__</span></tt> for the base class <tt class="docutils literal"><span class="pre">A</span></tt> immediately following <tt class="docutils literal"><span class="pre">B</span></tt> and then returns <tt class="docutils literal"><span class="pre">A.__dict__['m'].__get__(obj,</span> <span class="pre">A)</span></tt>. If not a descriptor, <tt class="docutils literal"><span class="pre">m</span></tt> is returned unchanged. If not in the dictionary, <tt class="docutils literal"><span class="pre">m</span></tt> reverts to a search using <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">object.__getattribute__()</span></tt></a>.</p> <p>Note, in Python 2.2, <tt class="docutils literal"><span class="pre">super(B,</span> <span class="pre">obj).m()</span></tt> would only invoke <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a> if <tt class="docutils literal"><span class="pre">m</span></tt> was a data descriptor. In Python 2.3, non-data descriptors also get invoked unless an old-style class is involved. The implementation details are in <tt class="xref c c-func docutils literal"><span class="pre">super_getattro()</span></tt> in <a class="reference external" href="http://svn.python.org/view/python/trunk/Objects/typeobject.c?view=markup">Objects/typeobject.c</a> and a pure Python equivalent can be found in <a class="reference external" href="http://www.python.org/2.2.3/descrintro.html#cooperation">Guido’s Tutorial</a>.</p> <p>The details above show that the mechanism for descriptors is embedded in the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">__getattribute__()</span></tt></a> methods for <a class="reference internal" href="../library/functions.html#object" title="object"><tt class="xref py py-class docutils literal"><span class="pre">object</span></tt></a>, <a class="reference internal" href="../library/functions.html#type" title="type"><tt class="xref py py-class docutils literal"><span class="pre">type</span></tt></a>, and <a class="reference internal" href="../library/functions.html#super" title="super"><tt class="xref py py-func docutils literal"><span class="pre">super()</span></tt></a>. Classes inherit this machinery when they derive from <a class="reference internal" href="../library/functions.html#object" title="object"><tt class="xref py py-class docutils literal"><span class="pre">object</span></tt></a> or if they have a meta-class providing similar functionality. Likewise, classes can turn-off descriptor invocation by overriding <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">__getattribute__()</span></tt></a>.</p> </div> <div class="section" id="descriptor-example"> <h2><a class="toc-backref" href="#id6">Descriptor Example</a><a class="headerlink" href="#descriptor-example" title="Permalink to this headline">¶</a></h2> <p>The following code creates a class whose objects are data descriptors which print a message for each get or set. Overriding <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><tt class="xref py py-meth docutils literal"><span class="pre">__getattribute__()</span></tt></a> is alternate approach that could do this for every attribute. However, this descriptor is useful for monitoring just a few chosen attributes:</p> <div class="highlight-python"><pre>class RevealAccess(object): """A data descriptor that sets and returns values normally and prints a message logging their access. """ def __init__(self, initval=None, name='var'): self.val = initval self.name = name def __get__(self, obj, objtype): print 'Retrieving', self.name return self.val def __set__(self, obj, val): print 'Updating' , self.name self.val = val >>> class MyClass(object): x = RevealAccess(10, 'var "x"') y = 5 >>> m = MyClass() >>> m.x Retrieving var "x" 10 >>> m.x = 20 Updating var "x" >>> m.x Retrieving var "x" 20 >>> m.y 5</pre> </div> <p>The protocol is simple and offers exciting possibilities. Several use cases are so common that they have been packaged into individual function calls. Properties, bound and unbound methods, static methods, and class methods are all based on the descriptor protocol.</p> </div> <div class="section" id="properties"> <h2><a class="toc-backref" href="#id7">Properties</a><a class="headerlink" href="#properties" title="Permalink to this headline">¶</a></h2> <p>Calling <a class="reference internal" href="../library/functions.html#property" title="property"><tt class="xref py py-func docutils literal"><span class="pre">property()</span></tt></a> is a succinct way of building a data descriptor that triggers function calls upon access to an attribute. Its signature is:</p> <div class="highlight-python"><pre>property(fget=None, fset=None, fdel=None, doc=None) -> property attribute</pre> </div> <p>The documentation shows a typical use to define a managed attribute <tt class="docutils literal"><span class="pre">x</span></tt>:</p> <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="k">def</span> <span class="nf">getx</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span> <span class="k">def</span> <span class="nf">setx</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span> <span class="o">=</span> <span class="n">value</span> <span class="k">def</span> <span class="nf">delx</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">__x</span> <span class="n">x</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">getx</span><span class="p">,</span> <span class="n">setx</span><span class="p">,</span> <span class="n">delx</span><span class="p">,</span> <span class="s">"I'm the 'x' property."</span><span class="p">)</span> </pre></div> </div> <p>To see how <a class="reference internal" href="../library/functions.html#property" title="property"><tt class="xref py py-func docutils literal"><span class="pre">property()</span></tt></a> is implemented in terms of the descriptor protocol, here is a pure Python equivalent:</p> <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Property</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="s">"Emulate PyProperty_Type() in Objects/descrobject.c"</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fget</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span> <span class="o">=</span> <span class="n">fget</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span> <span class="o">=</span> <span class="n">fset</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span> <span class="o">=</span> <span class="n">fdel</span> <span class="k">if</span> <span class="n">doc</span> <span class="ow">is</span> <span class="bp">None</span> <span class="ow">and</span> <span class="n">fget</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span> <span class="n">doc</span> <span class="o">=</span> <span class="n">fget</span><span class="o">.</span><span class="n">__doc__</span> <span class="bp">self</span><span class="o">.</span><span class="n">__doc__</span> <span class="o">=</span> <span class="n">doc</span> <span class="k">def</span> <span class="nf">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">return</span> <span class="bp">self</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s">"unreadable attribute"</span><span class="p">)</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s">"can't set attribute"</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span> <span class="k">def</span> <span class="nf">__delete__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s">"can't delete attribute"</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span> <span class="k">def</span> <span class="nf">getter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fget</span><span class="p">):</span> <span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="n">fget</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__doc__</span><span class="p">)</span> <span class="k">def</span> <span class="nf">setter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fset</span><span class="p">):</span> <span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">,</span> <span class="n">fset</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__doc__</span><span class="p">)</span> <span class="k">def</span> <span class="nf">deleter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fdel</span><span class="p">):</span> <span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)(</span><span class="bp">self</span><span class="o">.</span><span class="n">fget</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fset</span><span class="p">,</span> <span class="n">fdel</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__doc__</span><span class="p">)</span> </pre></div> </div> <p>The <a class="reference internal" href="../library/functions.html#property" title="property"><tt class="xref py py-func docutils literal"><span class="pre">property()</span></tt></a> builtin helps whenever a user interface has granted attribute access and then subsequent changes require the intervention of a method.</p> <p>For instance, a spreadsheet class may grant access to a cell value through <tt class="docutils literal"><span class="pre">Cell('b10').value</span></tt>. Subsequent improvements to the program require the cell to be recalculated on every access; however, the programmer does not want to affect existing client code accessing the attribute directly. The solution is to wrap access to the value attribute in a property data descriptor:</p> <div class="highlight-python"><pre>class Cell(object): . . . def getvalue(self, obj): "Recalculate cell before returning value" self.recalc() return obj._value value = property(getvalue)</pre> </div> </div> <div class="section" id="functions-and-methods"> <h2><a class="toc-backref" href="#id8">Functions and Methods</a><a class="headerlink" href="#functions-and-methods" title="Permalink to this headline">¶</a></h2> <p>Python’s object oriented features are built upon a function based environment. Using non-data descriptors, the two are merged seamlessly.</p> <p>Class dictionaries store methods as functions. In a class definition, methods are written using <a class="reference internal" href="../reference/compound_stmts.html#def"><tt class="xref std std-keyword docutils literal"><span class="pre">def</span></tt></a> and <a class="reference internal" href="../reference/expressions.html#lambda"><tt class="xref std std-keyword docutils literal"><span class="pre">lambda</span></tt></a>, the usual tools for creating functions. The only difference from regular functions is that the first argument is reserved for the object instance. By Python convention, the instance reference is called <em>self</em> but may be called <em>this</em> or any other variable name.</p> <p>To support method calls, functions include the <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a> method for binding methods during attribute access. This means that all functions are non-data descriptors which return bound or unbound methods depending whether they are invoked from an object or a class. In pure python, it works like this:</p> <div class="highlight-python"><pre>class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" return types.MethodType(self, obj, objtype)</pre> </div> <p>Running the interpreter shows how the function descriptor works in practice:</p> <div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="k">class</span> <span class="nc">D</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="go"> def f(self, x):</span> <span class="go"> return x</span> <span class="gp">>>> </span><span class="n">d</span> <span class="o">=</span> <span class="n">D</span><span class="p">()</span> <span class="gp">>>> </span><span class="n">D</span><span class="o">.</span><span class="n">__dict__</span><span class="p">[</span><span class="s">'f'</span><span class="p">]</span> <span class="c"># Stored internally as a function</span> <span class="go"><function f at 0x00C45070></span> <span class="gp">>>> </span><span class="n">D</span><span class="o">.</span><span class="n">f</span> <span class="c"># Get from a class becomes an unbound method</span> <span class="go"><unbound method D.f></span> <span class="gp">>>> </span><span class="n">d</span><span class="o">.</span><span class="n">f</span> <span class="c"># Get from an instance becomes a bound method</span> <span class="go"><bound method D.f of <__main__.D object at 0x00B18C90>></span> </pre></div> </div> <p>The output suggests that bound and unbound methods are two different types. While they could have been implemented that way, the actual C implementation of <a class="reference internal" href="../c-api/method.html#PyMethod_Type" title="PyMethod_Type"><tt class="xref c c-type docutils literal"><span class="pre">PyMethod_Type</span></tt></a> in <a class="reference external" href="http://svn.python.org/view/python/trunk/Objects/classobject.c?view=markup">Objects/classobject.c</a> is a single object with two different representations depending on whether the <tt class="xref py py-attr docutils literal"><span class="pre">im_self</span></tt> field is set or is <em>NULL</em> (the C equivalent of <em>None</em>).</p> <p>Likewise, the effects of calling a method object depend on the <tt class="xref py py-attr docutils literal"><span class="pre">im_self</span></tt> field. If set (meaning bound), the original function (stored in the <tt class="xref py py-attr docutils literal"><span class="pre">im_func</span></tt> field) is called as expected with the first argument set to the instance. If unbound, all of the arguments are passed unchanged to the original function. The actual C implementation of <tt class="xref py py-func docutils literal"><span class="pre">instancemethod_call()</span></tt> is only slightly more complex in that it includes some type checking.</p> </div> <div class="section" id="static-methods-and-class-methods"> <h2><a class="toc-backref" href="#id9">Static Methods and Class Methods</a><a class="headerlink" href="#static-methods-and-class-methods" title="Permalink to this headline">¶</a></h2> <p>Non-data descriptors provide a simple mechanism for variations on the usual patterns of binding functions into methods.</p> <p>To recap, functions have a <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><tt class="xref py py-meth docutils literal"><span class="pre">__get__()</span></tt></a> method so that they can be converted to a method when accessed as attributes. The non-data descriptor transforms a <tt class="docutils literal"><span class="pre">obj.f(*args)</span></tt> call into <tt class="docutils literal"><span class="pre">f(obj,</span> <span class="pre">*args)</span></tt>. Calling <tt class="docutils literal"><span class="pre">klass.f(*args)</span></tt> becomes <tt class="docutils literal"><span class="pre">f(*args)</span></tt>.</p> <p>This chart summarizes the binding and its two most useful variants:</p> <blockquote> <div><table border="1" class="docutils"> <colgroup> <col width="30%" /> <col width="39%" /> <col width="32%" /> </colgroup> <thead valign="bottom"> <tr class="row-odd"><th class="head">Transformation</th> <th class="head">Called from an Object</th> <th class="head">Called from a Class</th> </tr> </thead> <tbody valign="top"> <tr class="row-even"><td>function</td> <td>f(obj, *args)</td> <td>f(*args)</td> </tr> <tr class="row-odd"><td>staticmethod</td> <td>f(*args)</td> <td>f(*args)</td> </tr> <tr class="row-even"><td>classmethod</td> <td>f(type(obj), *args)</td> <td>f(klass, *args)</td> </tr> </tbody> </table> </div></blockquote> <p>Static methods return the underlying function without changes. Calling either <tt class="docutils literal"><span class="pre">c.f</span></tt> or <tt class="docutils literal"><span class="pre">C.f</span></tt> is the equivalent of a direct lookup into <tt class="docutils literal"><span class="pre">object.__getattribute__(c,</span> <span class="pre">"f")</span></tt> or <tt class="docutils literal"><span class="pre">object.__getattribute__(C,</span> <span class="pre">"f")</span></tt>. As a result, the function becomes identically accessible from either an object or a class.</p> <p>Good candidates for static methods are methods that do not reference the <tt class="docutils literal"><span class="pre">self</span></tt> variable.</p> <p>For instance, a statistics package may include a container class for experimental data. The class provides normal methods for computing the average, mean, median, and other descriptive statistics that depend on the data. However, there may be useful functions which are conceptually related but do not depend on the data. For instance, <tt class="docutils literal"><span class="pre">erf(x)</span></tt> is handy conversion routine that comes up in statistical work but does not directly depend on a particular dataset. It can be called either from an object or the class: <tt class="docutils literal"><span class="pre">s.erf(1.5)</span> <span class="pre">--></span> <span class="pre">.9332</span></tt> or <tt class="docutils literal"><span class="pre">Sample.erf(1.5)</span> <span class="pre">--></span> <span class="pre">.9332</span></tt>.</p> <p>Since staticmethods return the underlying function with no changes, the example calls are unexciting:</p> <div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="k">class</span> <span class="nc">E</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="go"> def f(x):</span> <span class="go"> print x</span> <span class="go"> f = staticmethod(f)</span> <span class="gp">>>> </span><span class="k">print</span> <span class="n">E</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="go">3</span> <span class="gp">>>> </span><span class="k">print</span> <span class="n">E</span><span class="p">()</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="go">3</span> </pre></div> </div> <p>Using the non-data descriptor protocol, a pure Python version of <a class="reference internal" href="../library/functions.html#staticmethod" title="staticmethod"><tt class="xref py py-func docutils literal"><span class="pre">staticmethod()</span></tt></a> would look like this:</p> <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">StaticMethod</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="s">"Emulate PyStaticMethod_Type() in Objects/funcobject.c"</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span> <span class="o">=</span> <span class="n">f</span> <span class="k">def</span> <span class="nf">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">objtype</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span> </pre></div> </div> <p>Unlike static methods, class methods prepend the class reference to the argument list before calling the function. This format is the same for whether the caller is an object or a class:</p> <div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="k">class</span> <span class="nc">E</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="go"> def f(klass, x):</span> <span class="go"> return klass.__name__, x</span> <span class="go"> f = classmethod(f)</span> <span class="gp">>>> </span><span class="k">print</span> <span class="n">E</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="go">('E', 3)</span> <span class="gp">>>> </span><span class="k">print</span> <span class="n">E</span><span class="p">()</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="go">('E', 3)</span> </pre></div> </div> <p>This behavior is useful whenever the function only needs to have a class reference and does not care about any underlying data. One use for classmethods is to create alternate class constructors. In Python 2.3, the classmethod <a class="reference internal" href="../library/stdtypes.html#dict.fromkeys" title="dict.fromkeys"><tt class="xref py py-func docutils literal"><span class="pre">dict.fromkeys()</span></tt></a> creates a new dictionary from a list of keys. The pure Python equivalent is:</p> <div class="highlight-python"><pre>class Dict(object): . . . def fromkeys(klass, iterable, value=None): "Emulate dict_fromkeys() in Objects/dictobject.c" d = klass() for key in iterable: d[key] = value return d fromkeys = classmethod(fromkeys)</pre> </div> <p>Now a new dictionary of unique keys can be constructed like this:</p> <div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">Dict</span><span class="o">.</span><span class="n">fromkeys</span><span class="p">(</span><span class="s">'abracadabra'</span><span class="p">)</span> <span class="go">{'a': None, 'r': None, 'b': None, 'c': None, 'd': None}</span> </pre></div> </div> <p>Using the non-data descriptor protocol, a pure Python version of <a class="reference internal" href="../library/functions.html#classmethod" title="classmethod"><tt class="xref py py-func docutils literal"><span class="pre">classmethod()</span></tt></a> would look like this:</p> <div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">ClassMethod</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="s">"Emulate PyClassMethod_Type() in Objects/funcobject.c"</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span> <span class="o">=</span> <span class="n">f</span> <span class="k">def</span> <span class="nf">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">klass</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="k">if</span> <span class="n">klass</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="n">klass</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span> <span class="k">def</span> <span class="nf">newfunc</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">(</span><span class="n">klass</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span> <span class="k">return</span> <span class="n">newfunc</span> </pre></div> </div> </div> </div> </div> </div> </div> <div class="sphinxsidebar"> <div class="sphinxsidebarwrapper"> <h3><a href="../contents.html">Table Of Contents</a></h3> <ul> <li><a class="reference internal" href="#">Descriptor HowTo Guide</a><ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#definition-and-introduction">Definition and Introduction</a></li> <li><a class="reference internal" href="#descriptor-protocol">Descriptor Protocol</a></li> <li><a class="reference internal" href="#invoking-descriptors">Invoking Descriptors</a></li> <li><a class="reference internal" href="#descriptor-example">Descriptor Example</a></li> <li><a class="reference internal" href="#properties">Properties</a></li> <li><a class="reference internal" href="#functions-and-methods">Functions and Methods</a></li> <li><a class="reference internal" href="#static-methods-and-class-methods">Static Methods and Class Methods</a></li> </ul> </li> </ul> <h4>Previous topic</h4> <p class="topless"><a href="curses.html" title="previous chapter">Curses Programming with Python</a></p> <h4>Next topic</h4> <p class="topless"><a href="doanddont.html" title="next chapter">Idioms and Anti-Idioms in Python</a></p> <h3>This Page</h3> <ul class="this-page-menu"> <li><a href="../bugs.html">Report a Bug</a></li> <li><a href="../_sources/howto/descriptor.txt" rel="nofollow">Show Source</a></li> </ul> <div id="searchbox" style="display: none"> <h3>Quick search</h3> <form class="search" action="../search.html" method="get"> <input type="text" name="q" /> <input type="submit" value="Go" /> <input type="hidden" name="check_keywords" value="yes" /> <input type="hidden" name="area" value="default" /> </form> <p class="searchtip" style="font-size: 90%"> Enter search terms or a module, class or function name. </p> </div> <script type="text/javascript">$('#searchbox').show(0);</script> </div> </div> <div class="clearer"></div> </div> <div class="related"> <h3>Navigation</h3> <ul> <li class="right" style="margin-right: 10px"> <a href="../genindex.html" title="General Index" >index</a></li> <li class="right" > <a href="../py-modindex.html" title="Python Module Index" >modules</a> |</li> <li class="right" > <a href="doanddont.html" title="Idioms and Anti-Idioms in Python" >next</a> |</li> <li class="right" > <a href="curses.html" title="Curses Programming with Python" >previous</a> |</li> <li><img src="../_static/py.png" alt="" style="vertical-align: middle; margin-top: -1px"/></li> <li><a href="http://www.python.org/">Python</a> »</li> <li> <a href="../index.html">Python 2.7.5 documentation</a> » </li> <li><a href="index.html" >Python HOWTOs</a> »</li> </ul> </div> <div class="footer"> © <a href="../copyright.html">Copyright</a> 1990-2020, Python Software Foundation. <br /> The Python Software Foundation is a non-profit corporation. <a href="http://www.python.org/psf/donations/">Please donate.</a> <br /> Last updated on Oct 13, 2020. <a href="../bugs.html">Found a bug</a>? <br /> Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3. </div> </body> </html>