636 lines
59 KiB
HTML
636 lines
59 KiB
HTML
|
|
|||
|
<!DOCTYPE html>
|
|||
|
|
|||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|||
|
<head>
|
|||
|
<meta charset="utf-8" />
|
|||
|
<title>Descriptor HowTo Guide — Python 3.7.4 documentation</title>
|
|||
|
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
|
|||
|
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
|||
|
|
|||
|
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></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/language_data.js"></script>
|
|||
|
|
|||
|
<script type="text/javascript" src="../_static/sidebar.js"></script>
|
|||
|
|
|||
|
<link rel="search" type="application/opensearchdescription+xml"
|
|||
|
title="Search within Python 3.7.4 documentation"
|
|||
|
href="../_static/opensearch.xml"/>
|
|||
|
<link rel="author" title="About these documents" href="../about.html" />
|
|||
|
<link rel="index" title="Index" href="../genindex.html" />
|
|||
|
<link rel="search" title="Search" href="../search.html" />
|
|||
|
<link rel="copyright" title="Copyright" href="../copyright.html" />
|
|||
|
<link rel="next" title="Functional Programming HOWTO" href="functional.html" />
|
|||
|
<link rel="prev" title="Curses Programming with Python" href="curses.html" />
|
|||
|
<link rel="shortcut icon" type="image/png" href="../_static/py.png" />
|
|||
|
<link rel="canonical" href="https://docs.python.org/3/howto/descriptor.html" />
|
|||
|
|
|||
|
<script type="text/javascript" src="../_static/copybutton.js"></script>
|
|||
|
<script type="text/javascript" src="../_static/switchers.js"></script>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<style>
|
|||
|
@media only screen {
|
|||
|
table.full-width-table {
|
|||
|
width: 100%;
|
|||
|
}
|
|||
|
}
|
|||
|
</style>
|
|||
|
|
|||
|
|
|||
|
</head><body>
|
|||
|
|
|||
|
<div class="related" role="navigation" aria-label="related navigation">
|
|||
|
<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="functional.html" title="Functional Programming HOWTO"
|
|||
|
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="https://www.python.org/">Python</a> »</li>
|
|||
|
<li>
|
|||
|
<span class="language_switcher_placeholder">en</span>
|
|||
|
<span class="version_switcher_placeholder">3.7.4</span>
|
|||
|
<a href="../index.html">Documentation </a> »
|
|||
|
</li>
|
|||
|
|
|||
|
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python HOWTOs</a> »</li>
|
|||
|
<li class="right">
|
|||
|
|
|||
|
|
|||
|
<div class="inline-search" style="display: none" role="search">
|
|||
|
<form class="inline-search" action="../search.html" method="get">
|
|||
|
<input placeholder="Quick search" 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>
|
|||
|
</div>
|
|||
|
<script type="text/javascript">$('.inline-search').show(0);</script>
|
|||
|
|
|
|||
|
</li>
|
|||
|
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="document">
|
|||
|
<div class="documentwrapper">
|
|||
|
<div class="bodywrapper">
|
|||
|
<div class="body" role="main">
|
|||
|
|
|||
|
<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>
|
|||
|
<dl class="field-list simple">
|
|||
|
<dt class="field-odd">Author</dt>
|
|||
|
<dd class="field-odd"><p>Raymond Hettinger</p>
|
|||
|
</dd>
|
|||
|
<dt class="field-even">Contact</dt>
|
|||
|
<dd class="field-even"><p><python at rcn dot com></p>
|
|||
|
</dd>
|
|||
|
</dl>
|
|||
|
<div class="contents topic" id="contents">
|
|||
|
<p class="topic-title first">Contents</p>
|
|||
|
<ul class="simple">
|
|||
|
<li><p><a class="reference internal" href="#descriptor-howto-guide" id="id1">Descriptor HowTo Guide</a></p>
|
|||
|
<ul>
|
|||
|
<li><p><a class="reference internal" href="#abstract" id="id2">Abstract</a></p></li>
|
|||
|
<li><p><a class="reference internal" href="#definition-and-introduction" id="id3">Definition and Introduction</a></p></li>
|
|||
|
<li><p><a class="reference internal" href="#descriptor-protocol" id="id4">Descriptor Protocol</a></p></li>
|
|||
|
<li><p><a class="reference internal" href="#invoking-descriptors" id="id5">Invoking Descriptors</a></p></li>
|
|||
|
<li><p><a class="reference internal" href="#descriptor-example" id="id6">Descriptor Example</a></p></li>
|
|||
|
<li><p><a class="reference internal" href="#properties" id="id7">Properties</a></p></li>
|
|||
|
<li><p><a class="reference internal" href="#functions-and-methods" id="id8">Functions and Methods</a></p></li>
|
|||
|
<li><p><a class="reference internal" href="#static-methods-and-class-methods" id="id9">Static Methods and Class Methods</a></p></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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>, <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a>, and
|
|||
|
<a class="reference internal" href="../reference/datamodel.html#object.__delete__" title="object.__delete__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__delete__()</span></code></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, <code class="docutils literal notranslate"><span class="pre">a.x</span></code> has a lookup chain
|
|||
|
starting with <code class="docutils literal notranslate"><span class="pre">a.__dict__['x']</span></code>, then <code class="docutils literal notranslate"><span class="pre">type(a).__dict__['x']</span></code>, and
|
|||
|
continuing through the base classes of <code class="docutils literal notranslate"><span class="pre">type(a)</span></code> 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.</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"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></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><code class="docutils literal notranslate"><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></code></p>
|
|||
|
<p><code class="docutils literal notranslate"><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></code></p>
|
|||
|
<p><code class="docutils literal notranslate"><span class="pre">descr.__delete__(self,</span> <span class="pre">obj)</span> <span class="pre">-></span> <span class="pre">None</span></code></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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> and <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a>, it is considered
|
|||
|
a data descriptor. Descriptors that only define <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> and
|
|||
|
<a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> with the <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></a> raising an <a class="reference internal" href="../library/exceptions.html#AttributeError" title="AttributeError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">AttributeError</span></code></a> when
|
|||
|
called. Defining the <a class="reference internal" href="../reference/datamodel.html#object.__set__" title="object.__set__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set__()</span></code></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,
|
|||
|
<code class="docutils literal notranslate"><span class="pre">d.__get__(obj)</span></code>.</p>
|
|||
|
<p>Alternatively, it is more common for a descriptor to be invoked automatically
|
|||
|
upon attribute access. For example, <code class="docutils literal notranslate"><span class="pre">obj.d</span></code> looks up <code class="docutils literal notranslate"><span class="pre">d</span></code> in the dictionary
|
|||
|
of <code class="docutils literal notranslate"><span class="pre">obj</span></code>. If <code class="docutils literal notranslate"><span class="pre">d</span></code> defines the method <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>, then <code class="docutils literal notranslate"><span class="pre">d.__get__(obj)</span></code>
|
|||
|
is invoked according to the precedence rules listed below.</p>
|
|||
|
<p>The details of invocation depend on whether <code class="docutils literal notranslate"><span class="pre">obj</span></code> is an object or a class.</p>
|
|||
|
<p>For objects, the machinery is in <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a> which
|
|||
|
transforms <code class="docutils literal notranslate"><span class="pre">b.x</span></code> into <code class="docutils literal notranslate"><span class="pre">type(b).__dict__['x'].__get__(b,</span> <span class="pre">type(b))</span></code>. 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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> if provided.
|
|||
|
The full C implementation can be found in <a class="reference internal" href="../c-api/object.html#c.PyObject_GenericGetAttr" title="PyObject_GenericGetAttr"><code class="xref c c-func docutils literal notranslate"><span class="pre">PyObject_GenericGetAttr()</span></code></a> in
|
|||
|
<a class="reference external" href="https://github.com/python/cpython/tree/3.7/Objects/object.c">Objects/object.c</a>.</p>
|
|||
|
<p>For classes, the machinery is in <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code> which transforms
|
|||
|
<code class="docutils literal notranslate"><span class="pre">B.x</span></code> into <code class="docutils literal notranslate"><span class="pre">B.__dict__['x'].__get__(None,</span> <span class="pre">B)</span></code>. In pure Python, it looks
|
|||
|
like:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><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="s2">"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="fm">__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="s1">'__get__'</span><span class="p">):</span>
|
|||
|
<span class="k">return</span> <span class="n">v</span><span class="o">.</span><span class="fm">__get__</span><span class="p">(</span><span class="kc">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><p>descriptors are invoked by the <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> method</p></li>
|
|||
|
<li><p>overriding <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> prevents automatic descriptor calls</p></li>
|
|||
|
<li><p><a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a> and <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code> make
|
|||
|
different calls to <a class="reference internal" href="../reference/datamodel.html#object.__get__" title="object.__get__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a>.</p></li>
|
|||
|
<li><p>data descriptors always override instance dictionaries.</p></li>
|
|||
|
<li><p>non-data descriptors may be overridden by instance dictionaries.</p></li>
|
|||
|
</ul>
|
|||
|
<p>The object returned by <code class="docutils literal notranslate"><span class="pre">super()</span></code> also has a custom <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a>
|
|||
|
method for invoking descriptors. The call <code class="docutils literal notranslate"><span class="pre">super(B,</span> <span class="pre">obj).m()</span></code> searches
|
|||
|
<code class="docutils literal notranslate"><span class="pre">obj.__class__.__mro__</span></code> for the base class <code class="docutils literal notranslate"><span class="pre">A</span></code> immediately following <code class="docutils literal notranslate"><span class="pre">B</span></code>
|
|||
|
and then returns <code class="docutils literal notranslate"><span class="pre">A.__dict__['m'].__get__(obj,</span> <span class="pre">B)</span></code>. If not a descriptor,
|
|||
|
<code class="docutils literal notranslate"><span class="pre">m</span></code> is returned unchanged. If not in the dictionary, <code class="docutils literal notranslate"><span class="pre">m</span></code> reverts to a
|
|||
|
search using <a class="reference internal" href="../reference/datamodel.html#object.__getattribute__" title="object.__getattribute__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code></a>.</p>
|
|||
|
<p>The implementation details are in <code class="xref c c-func docutils literal notranslate"><span class="pre">super_getattro()</span></code> in
|
|||
|
<a class="reference external" href="https://github.com/python/cpython/tree/3.7/Objects/typeobject.c">Objects/typeobject.c</a>. and a pure Python equivalent can be found in
|
|||
|
<a class="reference external" href="https://www.python.org/download/releases/2.2.3/descrintro/#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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></a> methods for <a class="reference internal" href="../library/functions.html#object" title="object"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></a>, <a class="reference internal" href="../library/functions.html#type" title="type"><code class="xref py py-class docutils literal notranslate"><span class="pre">type</span></code></a>, and
|
|||
|
<a class="reference internal" href="../library/functions.html#super" title="super"><code class="xref py py-func docutils literal notranslate"><span class="pre">super()</span></code></a>. Classes inherit this machinery when they derive from
|
|||
|
<a class="reference internal" href="../library/functions.html#object" title="object"><code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattribute__()</span></code></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-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">RevealAccess</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
|||
|
<span class="sd">"""A data descriptor that sets and returns values</span>
|
|||
|
<span class="sd"> normally and prints a message logging their access.</span>
|
|||
|
<span class="sd"> """</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">initval</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'var'</span><span class="p">):</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">val</span> <span class="o">=</span> <span class="n">initval</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</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="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s1">'Retrieving'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
|
|||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">val</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">val</span><span class="p">):</span>
|
|||
|
<span class="nb">print</span><span class="p">(</span><span class="s1">'Updating'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">val</span> <span class="o">=</span> <span class="n">val</span>
|
|||
|
|
|||
|
<span class="o">>>></span> <span class="k">class</span> <span class="nc">MyClass</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
|||
|
<span class="o">...</span> <span class="n">x</span> <span class="o">=</span> <span class="n">RevealAccess</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="s1">'var "x"'</span><span class="p">)</span>
|
|||
|
<span class="o">...</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">5</span>
|
|||
|
<span class="o">...</span>
|
|||
|
<span class="o">>>></span> <span class="n">m</span> <span class="o">=</span> <span class="n">MyClass</span><span class="p">()</span>
|
|||
|
<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">x</span>
|
|||
|
<span class="n">Retrieving</span> <span class="n">var</span> <span class="s2">"x"</span>
|
|||
|
<span class="mi">10</span>
|
|||
|
<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">20</span>
|
|||
|
<span class="n">Updating</span> <span class="n">var</span> <span class="s2">"x"</span>
|
|||
|
<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">x</span>
|
|||
|
<span class="n">Retrieving</span> <span class="n">var</span> <span class="s2">"x"</span>
|
|||
|
<span class="mi">20</span>
|
|||
|
<span class="o">>>></span> <span class="n">m</span><span class="o">.</span><span class="n">y</span>
|
|||
|
<span class="mi">5</span>
|
|||
|
</pre></div>
|
|||
|
</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 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"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></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-python3 notranslate"><div class="highlight"><pre><span></span><span class="nb">property</span><span class="p">(</span><span class="n">fget</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="nb">property</span> <span class="n">attribute</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The documentation shows a typical use to define a managed attribute <code class="docutils literal notranslate"><span class="pre">x</span></code>:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><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="s2">"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"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></a> is implemented in terms of the descriptor protocol,
|
|||
|
here is a pure Python equivalent:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><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="s2">"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="kc">None</span><span class="p">,</span> <span class="n">fset</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fdel</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="kc">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="kc">None</span> <span class="ow">and</span> <span class="n">fget</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">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="vm">__doc__</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="vm">__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="kc">None</span><span class="p">):</span>
|
|||
|
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">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="kc">None</span><span class="p">:</span>
|
|||
|
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">"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="kc">None</span><span class="p">:</span>
|
|||
|
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">"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="kc">None</span><span class="p">:</span>
|
|||
|
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="s2">"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="vm">__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="vm">__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="vm">__doc__</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The <a class="reference internal" href="../library/functions.html#property" title="property"><code class="xref py py-func docutils literal notranslate"><span class="pre">property()</span></code></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
|
|||
|
<code class="docutils literal notranslate"><span class="pre">Cell('b10').value</span></code>. 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-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Cell</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
|||
|
<span class="o">.</span> <span class="o">.</span> <span class="o">.</span>
|
|||
|
<span class="k">def</span> <span class="nf">getvalue</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="s2">"Recalculate the cell before returning value"</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">recalc</span><span class="p">()</span>
|
|||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_value</span>
|
|||
|
<span class="n">value</span> <span class="o">=</span> <span class="nb">property</span><span class="p">(</span><span class="n">getvalue</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</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"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">def</span></code></a> or <a class="reference internal" href="../reference/expressions.html#lambda"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">lambda</span></code></a>, the usual tools for
|
|||
|
creating functions. Methods only differ from regular functions in 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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method for
|
|||
|
binding methods during attribute access. This means that all functions are
|
|||
|
non-data descriptors which return bound methods when they are invoked from an
|
|||
|
object. In pure Python, it works like this:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Function</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
|||
|
<span class="o">.</span> <span class="o">.</span> <span class="o">.</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="kc">None</span><span class="p">):</span>
|
|||
|
<span class="s2">"Simulate func_descr_get() in Objects/funcobject.c"</span>
|
|||
|
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
|||
|
<span class="k">return</span> <span class="bp">self</span>
|
|||
|
<span class="k">return</span> <span class="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Running the interpreter shows how the function descriptor works in practice:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><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="gp">... </span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
|
|||
|
<span class="gp">... </span> <span class="k">return</span> <span class="n">x</span>
|
|||
|
<span class="gp">...</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="go"># Access through the class dictionary does not invoke __get__.</span>
|
|||
|
<span class="go"># It just returns the underlying function object.</span>
|
|||
|
<span class="gp">>>> </span><span class="n">D</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="s1">'f'</span><span class="p">]</span>
|
|||
|
<span class="go"><function D.f at 0x00C45070></span>
|
|||
|
|
|||
|
<span class="go"># Dotted access from a class calls __get__() which just returns</span>
|
|||
|
<span class="go"># the underlying function unchanged.</span>
|
|||
|
<span class="gp">>>> </span><span class="n">D</span><span class="o">.</span><span class="n">f</span>
|
|||
|
<span class="go"><function D.f at 0x00C45070></span>
|
|||
|
|
|||
|
<span class="go"># The function has a __qualname__ attribute to support introspection</span>
|
|||
|
<span class="gp">>>> </span><span class="n">D</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__qualname__</span>
|
|||
|
<span class="go">'D.f'</span>
|
|||
|
|
|||
|
<span class="go"># Dotted access from an instance calls __get__() which returns the</span>
|
|||
|
<span class="go"># function wrapped in a bound method object</span>
|
|||
|
<span class="gp">>>> </span><span class="n">d</span><span class="o">.</span><span class="n">f</span>
|
|||
|
<span class="go"><bound method D.f of <__main__.D object at 0x00B18C90>></span>
|
|||
|
|
|||
|
<span class="go"># Internally, the bound method stores the underlying function,</span>
|
|||
|
<span class="go"># the bound instance, and the class of the bound instance.</span>
|
|||
|
<span class="gp">>>> </span><span class="n">d</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__func__</span>
|
|||
|
<span class="go"><function D.f at 0x1012e5ae8></span>
|
|||
|
<span class="gp">>>> </span><span class="n">d</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__self__</span>
|
|||
|
<span class="go"><__main__.D object at 0x1012e1f98></span>
|
|||
|
<span class="gp">>>> </span><span class="n">d</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="vm">__class__</span>
|
|||
|
<span class="go"><class 'method'></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
</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__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__get__()</span></code></a> method so that they can be converted
|
|||
|
to a method when accessed as attributes. The non-data descriptor transforms an
|
|||
|
<code class="docutils literal notranslate"><span class="pre">obj.f(*args)</span></code> call into <code class="docutils literal notranslate"><span class="pre">f(obj,</span> <span class="pre">*args)</span></code>. Calling <code class="docutils literal notranslate"><span class="pre">klass.f(*args)</span></code>
|
|||
|
becomes <code class="docutils literal notranslate"><span class="pre">f(*args)</span></code>.</p>
|
|||
|
<p>This chart summarizes the binding and its two most useful variants:</p>
|
|||
|
<blockquote>
|
|||
|
<div><table class="docutils align-center">
|
|||
|
<colgroup>
|
|||
|
<col style="width: 30%" />
|
|||
|
<col style="width: 39%" />
|
|||
|
<col style="width: 32%" />
|
|||
|
</colgroup>
|
|||
|
<thead>
|
|||
|
<tr class="row-odd"><th class="head"><p>Transformation</p></th>
|
|||
|
<th class="head"><p>Called from an
|
|||
|
Object</p></th>
|
|||
|
<th class="head"><p>Called from a
|
|||
|
Class</p></th>
|
|||
|
</tr>
|
|||
|
</thead>
|
|||
|
<tbody>
|
|||
|
<tr class="row-even"><td><p>function</p></td>
|
|||
|
<td><p>f(obj, *args)</p></td>
|
|||
|
<td><p>f(*args)</p></td>
|
|||
|
</tr>
|
|||
|
<tr class="row-odd"><td><p>staticmethod</p></td>
|
|||
|
<td><p>f(*args)</p></td>
|
|||
|
<td><p>f(*args)</p></td>
|
|||
|
</tr>
|
|||
|
<tr class="row-even"><td><p>classmethod</p></td>
|
|||
|
<td><p>f(type(obj), *args)</p></td>
|
|||
|
<td><p>f(klass, *args)</p></td>
|
|||
|
</tr>
|
|||
|
</tbody>
|
|||
|
</table>
|
|||
|
</div></blockquote>
|
|||
|
<p>Static methods return the underlying function without changes. Calling either
|
|||
|
<code class="docutils literal notranslate"><span class="pre">c.f</span></code> or <code class="docutils literal notranslate"><span class="pre">C.f</span></code> is the equivalent of a direct lookup into
|
|||
|
<code class="docutils literal notranslate"><span class="pre">object.__getattribute__(c,</span> <span class="pre">"f")</span></code> or <code class="docutils literal notranslate"><span class="pre">object.__getattribute__(C,</span> <span class="pre">"f")</span></code>. 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
|
|||
|
<code class="docutils literal notranslate"><span class="pre">self</span></code> 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, <code class="docutils literal notranslate"><span class="pre">erf(x)</span></code> 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: <code class="docutils literal notranslate"><span class="pre">s.erf(1.5)</span> <span class="pre">--></span> <span class="pre">.9332</span></code> or
|
|||
|
<code class="docutils literal notranslate"><span class="pre">Sample.erf(1.5)</span> <span class="pre">--></span> <span class="pre">.9332</span></code>.</p>
|
|||
|
<p>Since staticmethods return the underlying function with no changes, the example
|
|||
|
calls are unexciting:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><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="gp">... </span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
|
|||
|
<span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
|
|||
|
<span class="gp">... </span> <span class="n">f</span> <span class="o">=</span> <span class="nb">staticmethod</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
|
|||
|
<span class="gp">...</span>
|
|||
|
<span class="gp">>>> </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="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"><code class="xref py py-func docutils literal notranslate"><span class="pre">staticmethod()</span></code></a> would look like this:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><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="s2">"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="kc">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-python3 notranslate"><div class="highlight"><pre><span></span><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="gp">... </span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">klass</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
|
|||
|
<span class="gp">... </span> <span class="k">return</span> <span class="n">klass</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">x</span>
|
|||
|
<span class="gp">... </span> <span class="n">f</span> <span class="o">=</span> <span class="nb">classmethod</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
|
|||
|
<span class="gp">...</span>
|
|||
|
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</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="nb">print</span><span class="p">(</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"><code class="xref py py-func docutils literal notranslate"><span class="pre">dict.fromkeys()</span></code></a> creates a new dictionary from a list of keys. The pure
|
|||
|
Python equivalent is:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Dict</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
|||
|
<span class="o">.</span> <span class="o">.</span> <span class="o">.</span>
|
|||
|
<span class="k">def</span> <span class="nf">fromkeys</span><span class="p">(</span><span class="n">klass</span><span class="p">,</span> <span class="n">iterable</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
|
|||
|
<span class="s2">"Emulate dict_fromkeys() in Objects/dictobject.c"</span>
|
|||
|
<span class="n">d</span> <span class="o">=</span> <span class="n">klass</span><span class="p">()</span>
|
|||
|
<span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">iterable</span><span class="p">:</span>
|
|||
|
<span class="n">d</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
|
|||
|
<span class="k">return</span> <span class="n">d</span>
|
|||
|
<span class="n">fromkeys</span> <span class="o">=</span> <span class="nb">classmethod</span><span class="p">(</span><span class="n">fromkeys</span><span class="p">)</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Now a new dictionary of unique keys can be constructed like this:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Dict</span><span class="o">.</span><span class="n">fromkeys</span><span class="p">(</span><span class="s1">'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"><code class="xref py py-func docutils literal notranslate"><span class="pre">classmethod()</span></code></a> would look like this:</p>
|
|||
|
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><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="s2">"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="kc">None</span><span class="p">):</span>
|
|||
|
<span class="k">if</span> <span class="n">klass</span> <span class="ow">is</span> <span class="kc">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" role="navigation" aria-label="main navigation">
|
|||
|
<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="functional.html"
|
|||
|
title="next chapter">Functional Programming HOWTO</a></p>
|
|||
|
<div role="note" aria-label="source link">
|
|||
|
<h3>This Page</h3>
|
|||
|
<ul class="this-page-menu">
|
|||
|
<li><a href="../bugs.html">Report a Bug</a></li>
|
|||
|
<li>
|
|||
|
<a href="https://github.com/python/cpython/blob/3.7/Doc/howto/descriptor.rst"
|
|||
|
rel="nofollow">Show Source
|
|||
|
</a>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="clearer"></div>
|
|||
|
</div>
|
|||
|
<div class="related" role="navigation" aria-label="related navigation">
|
|||
|
<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="functional.html" title="Functional Programming HOWTO"
|
|||
|
>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="https://www.python.org/">Python</a> »</li>
|
|||
|
<li>
|
|||
|
<span class="language_switcher_placeholder">en</span>
|
|||
|
<span class="version_switcher_placeholder">3.7.4</span>
|
|||
|
<a href="../index.html">Documentation </a> »
|
|||
|
</li>
|
|||
|
|
|||
|
<li class="nav-item nav-item-1"><a href="index.html" >Python HOWTOs</a> »</li>
|
|||
|
<li class="right">
|
|||
|
|
|||
|
|
|||
|
<div class="inline-search" style="display: none" role="search">
|
|||
|
<form class="inline-search" action="../search.html" method="get">
|
|||
|
<input placeholder="Quick search" 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>
|
|||
|
</div>
|
|||
|
<script type="text/javascript">$('.inline-search').show(0);</script>
|
|||
|
|
|
|||
|
</li>
|
|||
|
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<div class="footer">
|
|||
|
© <a href="../copyright.html">Copyright</a> 2001-2019, Python Software Foundation.
|
|||
|
<br />
|
|||
|
The Python Software Foundation is a non-profit corporation.
|
|||
|
<a href="https://www.python.org/psf/donations/">Please donate.</a>
|
|||
|
<br />
|
|||
|
Last updated on Jul 13, 2019.
|
|||
|
<a href="../bugs.html">Found a bug</a>?
|
|||
|
<br />
|
|||
|
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 2.0.1.
|
|||
|
</div>
|
|||
|
|
|||
|
</body>
|
|||
|
</html>
|