1636 lines
170 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en" data-content_root="../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta property="og:title" content="Descriptor Guide" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://docs.python.org/3/howto/descriptor.html" />
<meta property="og:site_name" content="Python documentation" />
<meta property="og:description" content="Author, Raymond Hettinger,, Contact,<python at rcn dot com>,. Contents: Descriptor Guide- Primer- Simple example: A descriptor that returns a constant, Dynamic lookups, Managed attributes, Customiz..." />
<meta property="og:image" content="https://docs.python.org/3/_static/og-image.png" />
<meta property="og:image:alt" content="Python documentation" />
<meta name="description" content="Author, Raymond Hettinger,, Contact,<python at rcn dot com>,. Contents: Descriptor Guide- Primer- Simple example: A descriptor that returns a constant, Dynamic lookups, Managed attributes, Customiz..." />
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="theme-color" content="#3776ab">
<title>Descriptor Guide &#8212; Python 3.13.3 documentation</title><meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="../_static/pydoctheme.css?v=23252803" />
<link id="pygments_dark_css" media="(prefers-color-scheme: dark)" rel="stylesheet" type="text/css" href="../_static/pygments_dark.css?v=5349f25f" />
<script src="../_static/documentation_options.js?v=5d57ca2d"></script>
<script src="../_static/doctools.js?v=9bcbadda"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../_static/sidebar.js"></script>
<link rel="search" type="application/opensearchdescription+xml"
title="Search within Python 3.13.3 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="Debugging C API extensions and CPython Internals with GDB" href="gdb_helpers.html" />
<link rel="prev" title="Curses Programming with Python" href="curses.html" />
<link rel="canonical" href="https://docs.python.org/3/howto/descriptor.html">
<style>
@media only screen {
table.full-width-table {
width: 100%;
}
}
</style>
<link rel="stylesheet" href="../_static/pydoctheme_dark.css" media="(prefers-color-scheme: dark)" id="pydoctheme_dark_css">
<link rel="shortcut icon" type="image/png" href="../_static/py.svg" />
<script type="text/javascript" src="../_static/copybutton.js"></script>
<script type="text/javascript" src="../_static/menu.js"></script>
<script type="text/javascript" src="../_static/search-focus.js"></script>
<script type="text/javascript" src="../_static/themetoggle.js"></script>
<script type="text/javascript" src="../_static/rtd_switcher.js"></script>
<meta name="readthedocs-addons-api-version" content="1">
</head>
<body>
<div class="mobile-nav">
<input type="checkbox" id="menuToggler" class="toggler__input" aria-controls="navigation"
aria-pressed="false" aria-expanded="false" role="button" aria-label="Menu" />
<nav class="nav-content" role="navigation">
<label for="menuToggler" class="toggler__label">
<span></span>
</label>
<span class="nav-items-wrapper">
<a href="https://www.python.org/" class="nav-logo">
<img src="../_static/py.svg" alt="Python logo"/>
</a>
<span class="version_switcher_placeholder"></span>
<form role="search" class="search" action="../search.html" method="get">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="search-icon">
<path fill-rule="nonzero" fill="currentColor" d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 001.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 00-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 005.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>
</svg>
<input placeholder="Quick search" aria-label="Quick search" type="search" name="q" />
<input type="submit" value="Go"/>
</form>
</span>
</nav>
<div class="menu-wrapper">
<nav class="menu" role="navigation" aria-label="main navigation">
<div class="language_switcher_placeholder"></div>
<label class="theme-selector-label">
Theme
<select class="theme-selector" oninput="activateTheme(this.value)">
<option value="auto" selected>Auto</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label>
<div>
<h3><a href="../contents.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Descriptor Guide</a><ul>
<li><a class="reference internal" href="#primer">Primer</a><ul>
<li><a class="reference internal" href="#simple-example-a-descriptor-that-returns-a-constant">Simple example: A descriptor that returns a constant</a></li>
<li><a class="reference internal" href="#dynamic-lookups">Dynamic lookups</a></li>
<li><a class="reference internal" href="#managed-attributes">Managed attributes</a></li>
<li><a class="reference internal" href="#customized-names">Customized names</a></li>
<li><a class="reference internal" href="#closing-thoughts">Closing thoughts</a></li>
</ul>
</li>
<li><a class="reference internal" href="#complete-practical-example">Complete Practical Example</a><ul>
<li><a class="reference internal" href="#validator-class">Validator class</a></li>
<li><a class="reference internal" href="#custom-validators">Custom validators</a></li>
<li><a class="reference internal" href="#practical-application">Practical application</a></li>
</ul>
</li>
<li><a class="reference internal" href="#technical-tutorial">Technical Tutorial</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="#overview-of-descriptor-invocation">Overview of descriptor invocation</a></li>
<li><a class="reference internal" href="#invocation-from-an-instance">Invocation from an instance</a></li>
<li><a class="reference internal" href="#invocation-from-a-class">Invocation from a class</a></li>
<li><a class="reference internal" href="#invocation-from-super">Invocation from super</a></li>
<li><a class="reference internal" href="#summary-of-invocation-logic">Summary of invocation logic</a></li>
<li><a class="reference internal" href="#automatic-name-notification">Automatic name notification</a></li>
<li><a class="reference internal" href="#orm-example">ORM example</a></li>
</ul>
</li>
<li><a class="reference internal" href="#pure-python-equivalents">Pure Python Equivalents</a><ul>
<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="#kinds-of-methods">Kinds of methods</a></li>
<li><a class="reference internal" href="#static-methods">Static methods</a></li>
<li><a class="reference internal" href="#class-methods">Class methods</a></li>
<li><a class="reference internal" href="#member-objects-and-slots">Member objects and __slots__</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<div>
<h4>Previous topic</h4>
<p class="topless"><a href="curses.html"
title="previous chapter">Curses Programming with Python</a></p>
</div>
<div>
<h4>Next topic</h4>
<p class="topless"><a href="gdb_helpers.html"
title="next chapter">Debugging C API extensions and CPython Internals with GDB</a></p>
</div>
<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/main/Doc/howto/descriptor.rst"
rel="nofollow">Show Source
</a>
</li>
</ul>
</div>
</nav>
</div>
</div>
<div class="related" role="navigation" aria-label="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="gdb_helpers.html" title="Debugging C API extensions and CPython Internals with GDB"
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.svg" alt="Python logo" style="vertical-align: middle; margin-top: -1px"/></li>
<li><a href="https://www.python.org/">Python</a> &#187;</li>
<li class="switchers">
<div class="language_switcher_placeholder"></div>
<div class="version_switcher_placeholder"></div>
</li>
<li>
</li>
<li id="cpython-language-and-version">
<a href="../index.html">3.13.3 Documentation</a> &#187;
</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python HOWTOs</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Descriptor Guide</a></li>
<li class="right">
<div class="inline-search" role="search">
<form class="inline-search" action="../search.html" method="get">
<input placeholder="Quick search" aria-label="Quick search" type="search" name="q" id="search-box" />
<input type="submit" value="Go" />
</form>
</div>
|
</li>
<li class="right">
<label class="theme-selector-label">
Theme
<select class="theme-selector" oninput="activateTheme(this.value)">
<option value="auto" selected>Auto</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label> |</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="descriptor-guide">
<span id="descriptorhowto"></span><h1><a class="toc-backref" href="#id1" role="doc-backlink">Descriptor Guide</a><a class="headerlink" href="#descriptor-guide" title="Link to this heading"></a></h1>
<dl class="field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd"><p>Raymond Hettinger</p>
</dd>
<dt class="field-even">Contact<span class="colon">:</span></dt>
<dd class="field-even"><p>&lt;python at rcn dot com&gt;</p>
</dd>
</dl>
<nav class="contents" id="contents">
<p class="topic-title">Contents</p>
<ul class="simple">
<li><p><a class="reference internal" href="#descriptor-guide" id="id1">Descriptor Guide</a></p>
<ul>
<li><p><a class="reference internal" href="#primer" id="id2">Primer</a></p>
<ul>
<li><p><a class="reference internal" href="#simple-example-a-descriptor-that-returns-a-constant" id="id3">Simple example: A descriptor that returns a constant</a></p></li>
<li><p><a class="reference internal" href="#dynamic-lookups" id="id4">Dynamic lookups</a></p></li>
<li><p><a class="reference internal" href="#managed-attributes" id="id5">Managed attributes</a></p></li>
<li><p><a class="reference internal" href="#customized-names" id="id6">Customized names</a></p></li>
<li><p><a class="reference internal" href="#closing-thoughts" id="id7">Closing thoughts</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#complete-practical-example" id="id8">Complete Practical Example</a></p>
<ul>
<li><p><a class="reference internal" href="#validator-class" id="id9">Validator class</a></p></li>
<li><p><a class="reference internal" href="#custom-validators" id="id10">Custom validators</a></p></li>
<li><p><a class="reference internal" href="#practical-application" id="id11">Practical application</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#technical-tutorial" id="id12">Technical Tutorial</a></p>
<ul>
<li><p><a class="reference internal" href="#abstract" id="id13">Abstract</a></p></li>
<li><p><a class="reference internal" href="#definition-and-introduction" id="id14">Definition and introduction</a></p></li>
<li><p><a class="reference internal" href="#descriptor-protocol" id="id15">Descriptor protocol</a></p></li>
<li><p><a class="reference internal" href="#overview-of-descriptor-invocation" id="id16">Overview of descriptor invocation</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-an-instance" id="id17">Invocation from an instance</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-a-class" id="id18">Invocation from a class</a></p></li>
<li><p><a class="reference internal" href="#invocation-from-super" id="id19">Invocation from super</a></p></li>
<li><p><a class="reference internal" href="#summary-of-invocation-logic" id="id20">Summary of invocation logic</a></p></li>
<li><p><a class="reference internal" href="#automatic-name-notification" id="id21">Automatic name notification</a></p></li>
<li><p><a class="reference internal" href="#orm-example" id="id22">ORM example</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#pure-python-equivalents" id="id23">Pure Python Equivalents</a></p>
<ul>
<li><p><a class="reference internal" href="#properties" id="id24">Properties</a></p></li>
<li><p><a class="reference internal" href="#functions-and-methods" id="id25">Functions and methods</a></p></li>
<li><p><a class="reference internal" href="#kinds-of-methods" id="id26">Kinds of methods</a></p></li>
<li><p><a class="reference internal" href="#static-methods" id="id27">Static methods</a></p></li>
<li><p><a class="reference internal" href="#class-methods" id="id28">Class methods</a></p></li>
<li><p><a class="reference internal" href="#member-objects-and-slots" id="id29">Member objects and __slots__</a></p></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
<p><a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">Descriptors</span></a> let objects customize attribute lookup,
storage, and deletion.</p>
<p>This guide has four major sections:</p>
<ol class="arabic simple">
<li><p>The “primer” gives a basic overview, moving gently from simple examples,
adding one feature at a time. Start here if youre new to descriptors.</p></li>
<li><p>The second section shows a complete, practical descriptor example. If you
already know the basics, start there.</p></li>
<li><p>The third section provides a more technical tutorial that goes into the
detailed mechanics of how descriptors work. Most people dont need this
level of detail.</p></li>
<li><p>The last section has pure Python equivalents for built-in descriptors that
are written in C. Read this if youre curious about how functions turn
into bound methods or about the implementation of common tools like
<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>, <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>, <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>, and
<a class="reference internal" href="../glossary.html#term-__slots__"><span class="xref std std-term">__slots__</span></a>.</p></li>
</ol>
<section id="primer">
<h2><a class="toc-backref" href="#id2" role="doc-backlink">Primer</a><a class="headerlink" href="#primer" title="Link to this heading"></a></h2>
<p>In this primer, we start with the most basic possible example and then well
add new capabilities one by one.</p>
<section id="simple-example-a-descriptor-that-returns-a-constant">
<h3><a class="toc-backref" href="#id3" role="doc-backlink">Simple example: A descriptor that returns a constant</a><a class="headerlink" href="#simple-example-a-descriptor-that-returns-a-constant" title="Link to this heading"></a></h3>
<p>The <code class="xref py py-class docutils literal notranslate"><span class="pre">Ten</span></code> class is a descriptor whose <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 always
returns the constant <code class="docutils literal notranslate"><span class="pre">10</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Ten</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="mi">10</span>
</pre></div>
</div>
<p>To use the descriptor, it must be stored as a class variable in another class:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">A</span><span class="p">:</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> <span class="c1"># Regular class attribute</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">Ten</span><span class="p">()</span> <span class="c1"># Descriptor instance</span>
</pre></div>
</div>
<p>An interactive session shows the difference between normal attribute lookup
and descriptor lookup:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span> <span class="c1"># Make an instance of class A</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">a</span><span class="o">.</span><span class="n">x</span> <span class="c1"># Normal attribute lookup</span>
<span class="go">5</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">a</span><span class="o">.</span><span class="n">y</span> <span class="c1"># Descriptor lookup</span>
<span class="go">10</span>
</pre></div>
</div>
<p>In the <code class="docutils literal notranslate"><span class="pre">a.x</span></code> attribute lookup, the dot operator finds <code class="docutils literal notranslate"><span class="pre">'x':</span> <span class="pre">5</span></code>
in the class dictionary. In the <code class="docutils literal notranslate"><span class="pre">a.y</span></code> lookup, the dot operator
finds a descriptor instance, recognized by its <code class="docutils literal notranslate"><span class="pre">__get__</span></code> method.
Calling that method returns <code class="docutils literal notranslate"><span class="pre">10</span></code>.</p>
<p>Note that the value <code class="docutils literal notranslate"><span class="pre">10</span></code> is not stored in either the class dictionary or the
instance dictionary. Instead, the value <code class="docutils literal notranslate"><span class="pre">10</span></code> is computed on demand.</p>
<p>This example shows how a simple descriptor works, but it isnt very useful.
For retrieving constants, normal attribute lookup would be better.</p>
<p>In the next section, well create something more useful, a dynamic lookup.</p>
</section>
<section id="dynamic-lookups">
<h3><a class="toc-backref" href="#id4" role="doc-backlink">Dynamic lookups</a><a class="headerlink" href="#dynamic-lookups" title="Link to this heading"></a></h3>
<p>Interesting descriptors typically run computations instead of returning
constants:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">os</span>
<span class="k">class</span><span class="w"> </span><span class="nc">DirectorySize</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="nb">len</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">dirname</span><span class="p">))</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Directory</span><span class="p">:</span>
<span class="n">size</span> <span class="o">=</span> <span class="n">DirectorySize</span><span class="p">()</span> <span class="c1"># Descriptor instance</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dirname</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">dirname</span> <span class="o">=</span> <span class="n">dirname</span> <span class="c1"># Regular instance attribute</span>
</pre></div>
</div>
<p>An interactive session shows that the lookup is dynamic — it computes
different, updated answers each time:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">s</span> <span class="o">=</span> <span class="n">Directory</span><span class="p">(</span><span class="s1">&#39;songs&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">g</span> <span class="o">=</span> <span class="n">Directory</span><span class="p">(</span><span class="s1">&#39;games&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">s</span><span class="o">.</span><span class="n">size</span> <span class="c1"># The songs directory has twenty files</span>
<span class="go">20</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">g</span><span class="o">.</span><span class="n">size</span> <span class="c1"># The games directory has three files</span>
<span class="go">3</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">os</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s1">&#39;games/chess&#39;</span><span class="p">)</span> <span class="c1"># Delete a game</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">g</span><span class="o">.</span><span class="n">size</span> <span class="c1"># File count is automatically updated</span>
<span class="go">2</span>
</pre></div>
</div>
<p>Besides showing how descriptors can run computations, this example also
reveals the purpose of the parameters 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>. The <em>self</em>
parameter is <em>size</em>, an instance of <em>DirectorySize</em>. The <em>obj</em> parameter is
either <em>g</em> or <em>s</em>, an instance of <em>Directory</em>. It is the <em>obj</em> parameter that
lets 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 learn the target directory. The <em>objtype</em>
parameter is the class <em>Directory</em>.</p>
</section>
<section id="managed-attributes">
<h3><a class="toc-backref" href="#id5" role="doc-backlink">Managed attributes</a><a class="headerlink" href="#managed-attributes" title="Link to this heading"></a></h3>
<p>A popular use for descriptors is managing access to instance data. The
descriptor is assigned to a public attribute in the class dictionary while the
actual data is stored as a private attribute in the instance dictionary. The
descriptors <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> methods are triggered when
the public attribute is accessed.</p>
<p>In the following example, <em>age</em> is the public attribute and <em>_age</em> is the
private attribute. When the public attribute is accessed, the descriptor logs
the lookup or update:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">LoggedAgeAccess</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="n">value</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">_age</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;Accessing </span><span class="si">%r</span><span class="s1"> giving </span><span class="si">%r</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;age&#39;</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;Updating </span><span class="si">%r</span><span class="s1"> to </span><span class="si">%r</span><span class="s1">&#39;</span><span class="p">,</span> <span class="s1">&#39;age&#39;</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="n">obj</span><span class="o">.</span><span class="n">_age</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Person</span><span class="p">:</span>
<span class="n">age</span> <span class="o">=</span> <span class="n">LoggedAgeAccess</span><span class="p">()</span> <span class="c1"># Descriptor instance</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</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="c1"># Regular instance attribute</span>
<span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span> <span class="c1"># Calls __set__()</span>
<span class="k">def</span><span class="w"> </span><span class="nf">birthday</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">age</span> <span class="o">+=</span> <span class="mi">1</span> <span class="c1"># Calls both __get__() and __set__()</span>
</pre></div>
</div>
<p>An interactive session shows that all access to the managed attribute <em>age</em> is
logged, but that the regular attribute <em>name</em> is not logged:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">mary</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">&#39;Mary M&#39;</span><span class="p">,</span> <span class="mi">30</span><span class="p">)</span> <span class="c1"># The initial age update is logged</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 30</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">dave</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">&#39;David D&#39;</span><span class="p">,</span> <span class="mi">40</span><span class="p">)</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 40</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">mary</span><span class="p">)</span> <span class="c1"># The actual data is in a private attribute</span>
<span class="go">{&#39;name&#39;: &#39;Mary M&#39;, &#39;_age&#39;: 30}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">dave</span><span class="p">)</span>
<span class="go">{&#39;name&#39;: &#39;David D&#39;, &#39;_age&#39;: 40}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mary</span><span class="o">.</span><span class="n">age</span> <span class="c1"># Access the data and log the lookup</span>
<span class="go">INFO:root:Accessing &#39;age&#39; giving 30</span>
<span class="go">30</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mary</span><span class="o">.</span><span class="n">birthday</span><span class="p">()</span> <span class="c1"># Updates are logged as well</span>
<span class="go">INFO:root:Accessing &#39;age&#39; giving 30</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 31</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">dave</span><span class="o">.</span><span class="n">name</span> <span class="c1"># Regular attribute lookup isn&#39;t logged</span>
<span class="go">&#39;David D&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">dave</span><span class="o">.</span><span class="n">age</span> <span class="c1"># Only the managed attribute is logged</span>
<span class="go">INFO:root:Accessing &#39;age&#39; giving 40</span>
<span class="go">40</span>
</pre></div>
</div>
<p>One major issue with this example is that the private name <em>_age</em> is hardwired in
the <em>LoggedAgeAccess</em> class. That means that each instance can only have one
logged attribute and that its name is unchangeable. In the next example,
well fix that problem.</p>
</section>
<section id="customized-names">
<h3><a class="toc-backref" href="#id6" role="doc-backlink">Customized names</a><a class="headerlink" href="#customized-names" title="Link to this heading"></a></h3>
<p>When a class uses descriptors, it can inform each descriptor about which
variable name was used.</p>
<p>In this example, the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class has two descriptor instances,
<em>name</em> and <em>age</em>. When the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class is defined, it makes a
callback to <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> in <em>LoggedAccess</em> so that the field names can
be recorded, giving each descriptor its own <em>public_name</em> and <em>private_name</em>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">logging</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">LoggedAccess</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">public_name</span> <span class="o">=</span> <span class="n">name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">private_name</span> <span class="o">=</span> <span class="s1">&#39;_&#39;</span> <span class="o">+</span> <span class="n">name</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="n">value</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">)</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;Accessing </span><span class="si">%r</span><span class="s1"> giving </span><span class="si">%r</span><span class="s1">&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">public_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;Updating </span><span class="si">%r</span><span class="s1"> to </span><span class="si">%r</span><span class="s1">&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">public_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="nb">setattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Person</span><span class="p">:</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">LoggedAccess</span><span class="p">()</span> <span class="c1"># First descriptor instance</span>
<span class="n">age</span> <span class="o">=</span> <span class="n">LoggedAccess</span><span class="p">()</span> <span class="c1"># Second descriptor instance</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</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="c1"># Calls the first descriptor</span>
<span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span> <span class="c1"># Calls the second descriptor</span>
<span class="k">def</span><span class="w"> </span><span class="nf">birthday</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">age</span> <span class="o">+=</span> <span class="mi">1</span>
</pre></div>
</div>
<p>An interactive session shows that the <code class="xref py py-class docutils literal notranslate"><span class="pre">Person</span></code> class has called
<a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> so that the field names would be recorded. Here
we call <a class="reference internal" href="../library/functions.html#vars" title="vars"><code class="xref py py-func docutils literal notranslate"><span class="pre">vars()</span></code></a> to look up the descriptor without triggering it:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="nb">vars</span><span class="p">(</span><span class="n">Person</span><span class="p">)[</span><span class="s1">&#39;name&#39;</span><span class="p">])</span>
<span class="go">{&#39;public_name&#39;: &#39;name&#39;, &#39;private_name&#39;: &#39;_name&#39;}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="nb">vars</span><span class="p">(</span><span class="n">Person</span><span class="p">)[</span><span class="s1">&#39;age&#39;</span><span class="p">])</span>
<span class="go">{&#39;public_name&#39;: &#39;age&#39;, &#39;private_name&#39;: &#39;_age&#39;}</span>
</pre></div>
</div>
<p>The new class now logs access to both <em>name</em> and <em>age</em>:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">pete</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">&#39;Peter P&#39;</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="go">INFO:root:Updating &#39;name&#39; to &#39;Peter P&#39;</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 10</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">kate</span> <span class="o">=</span> <span class="n">Person</span><span class="p">(</span><span class="s1">&#39;Catherine C&#39;</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
<span class="go">INFO:root:Updating &#39;name&#39; to &#39;Catherine C&#39;</span>
<span class="go">INFO:root:Updating &#39;age&#39; to 20</span>
</pre></div>
</div>
<p>The two <em>Person</em> instances contain only the private names:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">pete</span><span class="p">)</span>
<span class="go">{&#39;_name&#39;: &#39;Peter P&#39;, &#39;_age&#39;: 10}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">kate</span><span class="p">)</span>
<span class="go">{&#39;_name&#39;: &#39;Catherine C&#39;, &#39;_age&#39;: 20}</span>
</pre></div>
</div>
</section>
<section id="closing-thoughts">
<h3><a class="toc-backref" href="#id7" role="doc-backlink">Closing thoughts</a><a class="headerlink" href="#closing-thoughts" title="Link to this heading"></a></h3>
<p>A <a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">descriptor</span></a> is what we call any object that defines <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>, or <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>.</p>
<p>Optionally, descriptors can have a <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> method. This is only
used in cases where a descriptor needs to know either the class where it was
created or the name of class variable it was assigned to. (This method, if
present, is called even if the class is not a descriptor.)</p>
<p>Descriptors get invoked by the dot operator during attribute lookup. If a
descriptor is accessed indirectly with <code class="docutils literal notranslate"><span class="pre">vars(some_class)[descriptor_name]</span></code>,
the descriptor instance is returned without invoking it.</p>
<p>Descriptors only work when used as class variables. When put in instances,
they have no effect.</p>
<p>The main motivation for descriptors is to provide a hook allowing objects
stored in class variables to control what happens during attribute lookup.</p>
<p>Traditionally, the calling class controls what happens during lookup.
Descriptors invert that relationship and allow the data being looked-up to
have a say in the matter.</p>
<p>Descriptors are used throughout the language. It is how functions turn into
bound methods. Common tools like <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>, <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>,
<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>, and <a class="reference internal" href="../library/functools.html#functools.cached_property" title="functools.cached_property"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.cached_property()</span></code></a> are all implemented as
descriptors.</p>
</section>
</section>
<section id="complete-practical-example">
<h2><a class="toc-backref" href="#id8" role="doc-backlink">Complete Practical Example</a><a class="headerlink" href="#complete-practical-example" title="Link to this heading"></a></h2>
<p>In this example, we create a practical and powerful tool for locating
notoriously hard to find data corruption bugs.</p>
<section id="validator-class">
<h3><a class="toc-backref" href="#id9" role="doc-backlink">Validator class</a><a class="headerlink" href="#validator-class" title="Link to this heading"></a></h3>
<p>A validator is a descriptor for managed attribute access. Prior to storing
any data, it verifies that the new value meets various type and range
restrictions. If those restrictions arent met, it raises an exception to
prevent data corruption at its source.</p>
<p>This <code class="xref py py-class docutils literal notranslate"><span class="pre">Validator</span></code> class is both an <a class="reference internal" href="../glossary.html#term-abstract-base-class"><span class="xref std std-term">abstract base class</span></a> and a
managed attribute descriptor:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Validator</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">private_name</span> <span class="o">=</span> <span class="s1">&#39;_&#39;</span> <span class="o">+</span> <span class="n">name</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="bp">self</span><span class="o">.</span><span class="n">validate</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="nb">setattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="nd">@abstractmethod</span>
<span class="k">def</span><span class="w"> </span><span class="nf">validate</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="k">pass</span>
</pre></div>
</div>
<p>Custom validators need to inherit from <code class="xref py py-class docutils literal notranslate"><span class="pre">Validator</span></code> and must supply a
<code class="xref py py-meth docutils literal notranslate"><span class="pre">validate()</span></code> method to test various restrictions as needed.</p>
</section>
<section id="custom-validators">
<h3><a class="toc-backref" href="#id10" role="doc-backlink">Custom validators</a><a class="headerlink" href="#custom-validators" title="Link to this heading"></a></h3>
<p>Here are three practical data validation utilities:</p>
<ol class="arabic simple">
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">OneOf</span></code> verifies that a value is one of a restricted set of options.</p></li>
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">Number</span></code> verifies that a value is either an <a class="reference internal" href="../library/functions.html#int" title="int"><code class="xref py py-class docutils literal notranslate"><span class="pre">int</span></code></a> or
<a class="reference internal" href="../library/functions.html#float" title="float"><code class="xref py py-class docutils literal notranslate"><span class="pre">float</span></code></a>. Optionally, it verifies that a value is between a given
minimum or maximum.</p></li>
<li><p><code class="xref py py-class docutils literal notranslate"><span class="pre">String</span></code> verifies that a value is a <a class="reference internal" href="../library/stdtypes.html#str" title="str"><code class="xref py py-class docutils literal notranslate"><span class="pre">str</span></code></a>. Optionally, it
validates a given minimum or maximum length. It can validate a
user-defined <a class="reference external" href="https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)">predicate</a> as well.</p></li>
</ol>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">OneOf</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">options</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="nf">validate</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="k">if</span> <span class="n">value</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be one of </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="si">!r}</span><span class="s1">&#39;</span>
<span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Number</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">minvalue</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxvalue</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">minvalue</span> <span class="o">=</span> <span class="n">minvalue</span>
<span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span> <span class="o">=</span> <span class="n">maxvalue</span>
<span class="k">def</span><span class="w"> </span><span class="nf">validate</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="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">)):</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be an int or float&#39;</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">value</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be at least </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">minvalue</span><span class="si">!r}</span><span class="s1">&#39;</span>
<span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">value</span> <span class="o">&gt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no more than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">maxvalue</span><span class="si">!r}</span><span class="s1">&#39;</span>
<span class="p">)</span>
<span class="k">class</span><span class="w"> </span><span class="nc">String</span><span class="p">(</span><span class="n">Validator</span><span class="p">):</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">minsize</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">maxsize</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">predicate</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">minsize</span> <span class="o">=</span> <span class="n">minsize</span>
<span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span> <span class="o">=</span> <span class="n">maxsize</span>
<span class="bp">self</span><span class="o">.</span><span class="n">predicate</span> <span class="o">=</span> <span class="n">predicate</span>
<span class="k">def</span><span class="w"> </span><span class="nf">validate</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="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be an str&#39;</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">minsize</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">minsize</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no smaller than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">minsize</span><span class="si">!r}</span><span class="s1">&#39;</span>
<span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">&gt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1"> to be no bigger than </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">maxsize</span><span class="si">!r}</span><span class="s1">&#39;</span>
<span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">predicate</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">predicate</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;Expected </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">predicate</span><span class="si">}</span><span class="s1"> to be true for </span><span class="si">{</span><span class="n">value</span><span class="si">!r}</span><span class="s1">&#39;</span>
<span class="p">)</span>
</pre></div>
</div>
</section>
<section id="practical-application">
<h3><a class="toc-backref" href="#id11" role="doc-backlink">Practical application</a><a class="headerlink" href="#practical-application" title="Link to this heading"></a></h3>
<p>Heres how the data validators can be used in a real class:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Component</span><span class="p">:</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">String</span><span class="p">(</span><span class="n">minsize</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">maxsize</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">predicate</span><span class="o">=</span><span class="nb">str</span><span class="o">.</span><span class="n">isupper</span><span class="p">)</span>
<span class="n">kind</span> <span class="o">=</span> <span class="n">OneOf</span><span class="p">(</span><span class="s1">&#39;wood&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="s1">&#39;plastic&#39;</span><span class="p">)</span>
<span class="n">quantity</span> <span class="o">=</span> <span class="n">Number</span><span class="p">(</span><span class="n">minvalue</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">kind</span><span class="p">,</span> <span class="n">quantity</span><span class="p">):</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="bp">self</span><span class="o">.</span><span class="n">kind</span> <span class="o">=</span> <span class="n">kind</span>
<span class="bp">self</span><span class="o">.</span><span class="n">quantity</span> <span class="o">=</span> <span class="n">quantity</span>
</pre></div>
</div>
<p>The descriptors prevent invalid instances from being created:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">Component</span><span class="p">(</span><span class="s1">&#39;Widget&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="c1"># Blocked: &#39;Widget&#39; is not all uppercase</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected &lt;method &#39;isupper&#39; of &#39;str&#39; objects&gt; to be true for &#39;Widget&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Component</span><span class="p">(</span><span class="s1">&#39;WIDGET&#39;</span><span class="p">,</span> <span class="s1">&#39;metle&#39;</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="c1"># Blocked: &#39;metle&#39; is misspelled</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected &#39;metle&#39; to be one of {&#39;metal&#39;, &#39;plastic&#39;, &#39;wood&#39;}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Component</span><span class="p">(</span><span class="s1">&#39;WIDGET&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="o">-</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># Blocked: -5 is negative</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">ValueError</span>: <span class="n">Expected -5 to be at least 0</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Component</span><span class="p">(</span><span class="s1">&#39;WIDGET&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="s1">&#39;V&#39;</span><span class="p">)</span> <span class="c1"># Blocked: &#39;V&#39; isn&#39;t a number</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">TypeError</span>: <span class="n">Expected &#39;V&#39; to be an int or float</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">c</span> <span class="o">=</span> <span class="n">Component</span><span class="p">(</span><span class="s1">&#39;WIDGET&#39;</span><span class="p">,</span> <span class="s1">&#39;metal&#39;</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="c1"># Allowed: The inputs are valid</span>
</pre></div>
</div>
</section>
</section>
<section id="technical-tutorial">
<h2><a class="toc-backref" href="#id12" role="doc-backlink">Technical Tutorial</a><a class="headerlink" href="#technical-tutorial" title="Link to this heading"></a></h2>
<p>What follows is a more technical tutorial for the mechanics and details of how
descriptors work.</p>
<section id="abstract">
<h3><a class="toc-backref" href="#id13" role="doc-backlink">Abstract</a><a class="headerlink" href="#abstract" title="Link to this heading"></a></h3>
<p>Defines descriptors, summarizes the protocol, and shows how descriptors are
called. Provides an example showing how object relational mappings work.</p>
<p>Learning about descriptors not only provides access to a larger toolset, it
creates a deeper understanding of how Python works.</p>
</section>
<section id="definition-and-introduction">
<h3><a class="toc-backref" href="#id14" role="doc-backlink">Definition and introduction</a><a class="headerlink" href="#definition-and-introduction" title="Link to this heading"></a></h3>
<p>In general, a descriptor is an attribute value that has one of the 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
attribute, it is said to be a <a class="reference internal" href="../glossary.html#term-descriptor"><span class="xref std std-term">descriptor</span></a>.</p>
<p>The default behavior for attribute access is to get, set, or delete the
attribute from an objects 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 method resolution order of <code class="docutils literal notranslate"><span class="pre">type(a)</span></code>. 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. Descriptors
simplify the underlying C code and offer a flexible set of new tools for
everyday Python programs.</p>
</section>
<section id="descriptor-protocol">
<h3><a class="toc-backref" href="#id15" role="doc-backlink">Descriptor protocol</a><a class="headerlink" href="#descriptor-protocol" title="Link to this heading"></a></h3>
<p><code class="docutils literal notranslate"><span class="pre">descr.__get__(self,</span> <span class="pre">obj,</span> <span class="pre">type=None)</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></code></p>
<p><code class="docutils literal notranslate"><span class="pre">descr.__delete__(self,</span> <span class="pre">obj)</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 <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> or <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>, 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 often 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 instances dictionary. If an instances dictionary
has an entry with the same name as a data descriptor, the data descriptor
takes precedence. If an instances 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>
</section>
<section id="overview-of-descriptor-invocation">
<h3><a class="toc-backref" href="#id16" role="doc-backlink">Overview of descriptor invocation</a><a class="headerlink" href="#overview-of-descriptor-invocation" title="Link to this heading"></a></h3>
<p>A descriptor can be called directly with <code class="docutils literal notranslate"><span class="pre">desc.__get__(obj)</span></code> or
<code class="docutils literal notranslate"><span class="pre">desc.__get__(None,</span> <span class="pre">cls)</span></code>.</p>
<p>But it is more common for a descriptor to be invoked automatically from
attribute access.</p>
<p>The expression <code class="docutils literal notranslate"><span class="pre">obj.x</span></code> looks up the attribute <code class="docutils literal notranslate"><span class="pre">x</span></code> in the chain of
namespaces for <code class="docutils literal notranslate"><span class="pre">obj</span></code>. If the search finds a descriptor outside of the
instance <a class="reference internal" href="../reference/datamodel.html#object.__dict__" title="object.__dict__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__dict__</span></code></a>, its <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 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, class, or
instance of super.</p>
</section>
<section id="invocation-from-an-instance">
<h3><a class="toc-backref" href="#id17" role="doc-backlink">Invocation from an instance</a><a class="headerlink" href="#invocation-from-an-instance" title="Link to this heading"></a></h3>
<p>Instance lookup scans through a chain of namespaces giving data descriptors
the highest priority, followed by instance variables, then non-data
descriptors, then class variables, and lastly <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 it is
provided.</p>
<p>If a descriptor is found for <code class="docutils literal notranslate"><span class="pre">a.x</span></code>, then it is invoked with:
<code class="docutils literal notranslate"><span class="pre">desc.__get__(a,</span> <span class="pre">type(a))</span></code>.</p>
<p>The logic for a dotted lookup 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>. Here is
a pure Python equivalent:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">find_name_in_mro</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">default</span><span class="p">):</span>
<span class="s2">&quot;Emulate _PyType_Lookup() in Objects/typeobject.c&quot;</span>
<span class="k">for</span> <span class="n">base</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__mro__</span><span class="p">:</span>
<span class="k">if</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">vars</span><span class="p">(</span><span class="n">base</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">vars</span><span class="p">(</span><span class="n">base</span><span class="p">)[</span><span class="n">name</span><span class="p">]</span>
<span class="k">return</span> <span class="n">default</span>
<span class="k">def</span><span class="w"> </span><span class="nf">object_getattribute</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="s2">&quot;Emulate PyObject_GenericGetAttr() in Objects/object.c&quot;</span>
<span class="n">null</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>
<span class="n">objtype</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="n">cls_var</span> <span class="o">=</span> <span class="n">find_name_in_mro</span><span class="p">(</span><span class="n">objtype</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">null</span><span class="p">)</span>
<span class="n">descr_get</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">&#39;__get__&#39;</span><span class="p">,</span> <span class="n">null</span><span class="p">)</span>
<span class="k">if</span> <span class="n">descr_get</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">&#39;__set__&#39;</span><span class="p">)</span>
<span class="ow">or</span> <span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">cls_var</span><span class="p">),</span> <span class="s1">&#39;__delete__&#39;</span><span class="p">)):</span>
<span class="k">return</span> <span class="n">descr_get</span><span class="p">(</span><span class="n">cls_var</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="c1"># data descriptor</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s1">&#39;__dict__&#39;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">vars</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">vars</span><span class="p">(</span><span class="n">obj</span><span class="p">)[</span><span class="n">name</span><span class="p">]</span> <span class="c1"># instance variable</span>
<span class="k">if</span> <span class="n">descr_get</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
<span class="k">return</span> <span class="n">descr_get</span><span class="p">(</span><span class="n">cls_var</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="c1"># non-data descriptor</span>
<span class="k">if</span> <span class="n">cls_var</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">null</span><span class="p">:</span>
<span class="k">return</span> <span class="n">cls_var</span> <span class="c1"># class variable</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre></div>
</div>
<p>Note, there is no <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> hook 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>
code. That is why calling <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> directly or with
<code class="docutils literal notranslate"><span class="pre">super().__getattribute__</span></code> will bypass <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> entirely.</p>
<p>Instead, it is the dot operator and the <a class="reference internal" href="../library/functions.html#getattr" title="getattr"><code class="xref py py-func docutils literal notranslate"><span class="pre">getattr()</span></code></a> function that are
responsible for invoking <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> whenever <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>
raises 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>. Their logic is encapsulated in a helper
function:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">getattr_hook</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="s2">&quot;Emulate slot_tp_getattr_hook() in Objects/typeobject.c&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">obj</span><span class="o">.</span><span class="fm">__getattribute__</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">),</span> <span class="s1">&#39;__getattr__&#39;</span><span class="p">):</span>
<span class="k">raise</span>
<span class="k">return</span> <span class="nb">type</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span><span class="o">.</span><span class="fm">__getattr__</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="c1"># __getattr__</span>
</pre></div>
</div>
</section>
<section id="invocation-from-a-class">
<h3><a class="toc-backref" href="#id18" role="doc-backlink">Invocation from a class</a><a class="headerlink" href="#invocation-from-a-class" title="Link to this heading"></a></h3>
<p>The logic for a dotted lookup such as <code class="docutils literal notranslate"><span class="pre">A.x</span></code> is in
<code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__getattribute__()</span></code>. The steps are similar to those for
<code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code> but the instance dictionary lookup is replaced
by a search through the classs <a class="reference internal" href="../glossary.html#term-method-resolution-order"><span class="xref std std-term">method resolution order</span></a>.</p>
<p>If a descriptor is found, it is invoked with <code class="docutils literal notranslate"><span class="pre">desc.__get__(None,</span> <span class="pre">A)</span></code>.</p>
<p>The full C implementation can be found in <code class="xref c c-func docutils literal notranslate"><span class="pre">type_getattro()</span></code> and
<code class="xref c c-func docutils literal notranslate"><span class="pre">_PyType_Lookup()</span></code> in <a class="extlink-source reference external" href="https://github.com/python/cpython/tree/3.13/Objects/typeobject.c">Objects/typeobject.c</a>.</p>
</section>
<section id="invocation-from-super">
<h3><a class="toc-backref" href="#id19" role="doc-backlink">Invocation from super</a><a class="headerlink" href="#invocation-from-super" title="Link to this heading"></a></h3>
<p>The logic for supers dotted lookup is 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> method for
object returned by <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>.</p>
<p>A dotted lookup such as <code class="docutils literal notranslate"><span class="pre">super(A,</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">B</span></code> immediately following <code class="docutils literal notranslate"><span class="pre">A</span></code> and then returns
<code class="docutils literal notranslate"><span class="pre">B.__dict__['m'].__get__(obj,</span> <span class="pre">A)</span></code>. If not a descriptor, <code class="docutils literal notranslate"><span class="pre">m</span></code> is returned
unchanged.</p>
<p>The full C implementation can be found in <code class="xref c c-func docutils literal notranslate"><span class="pre">super_getattro()</span></code> in
<a class="extlink-source reference external" href="https://github.com/python/cpython/tree/3.13/Objects/typeobject.c">Objects/typeobject.c</a>. A pure Python equivalent can be found in
<a class="reference external" href="https://www.python.org/download/releases/2.2.3/descrintro/#cooperation">Guidos Tutorial</a>.</p>
</section>
<section id="summary-of-invocation-logic">
<h3><a class="toc-backref" href="#id20" role="doc-backlink">Summary of invocation logic</a><a class="headerlink" href="#summary-of-invocation-logic" title="Link to this heading"></a></h3>
<p>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>.</p>
<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>Classes inherit this machinery 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>, <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>, or
<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>.</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
because all the descriptor logic is in that method.</p></li>
<li><p><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__getattribute__()</span></code> 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>. The first includes the instance and may
include the class. The second puts in <code class="docutils literal notranslate"><span class="pre">None</span></code> for the instance and always
includes the class.</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>
</section>
<section id="automatic-name-notification">
<h3><a class="toc-backref" href="#id21" role="doc-backlink">Automatic name notification</a><a class="headerlink" href="#automatic-name-notification" title="Link to this heading"></a></h3>
<p>Sometimes it is desirable for a descriptor to know what class variable name it
was assigned to. When a new class is created, the <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> metaclass
scans the dictionary of the new class. If any of the entries are descriptors
and if they define <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a>, that method is called with two
arguments. The <em>owner</em> is the class where the descriptor is used, and the
<em>name</em> is the class variable the descriptor was assigned to.</p>
<p>The implementation details are in <code class="xref c c-func docutils literal notranslate"><span class="pre">type_new()</span></code> and
<code class="xref c c-func docutils literal notranslate"><span class="pre">set_names()</span></code> in <a class="extlink-source reference external" href="https://github.com/python/cpython/tree/3.13/Objects/typeobject.c">Objects/typeobject.c</a>.</p>
<p>Since the update logic is in <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__new__()</span></code>, notifications only take
place at the time of class creation. If descriptors are added to the class
afterwards, <a class="reference internal" href="../reference/datamodel.html#object.__set_name__" title="object.__set_name__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__set_name__()</span></code></a> will need to be called manually.</p>
</section>
<section id="orm-example">
<h3><a class="toc-backref" href="#id22" role="doc-backlink">ORM example</a><a class="headerlink" href="#orm-example" title="Link to this heading"></a></h3>
<p>The following code is a simplified skeleton showing how data descriptors could
be used to implement an <a class="reference external" href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping">object relational mapping</a>.</p>
<p>The essential idea is that the data is stored in an external database. The
Python instances only hold keys to the databases tables. Descriptors take
care of lookups or updates:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Field</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">fetch</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;SELECT </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1"> FROM </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">table</span><span class="si">}</span><span class="s1"> WHERE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s1">=?;&#39;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">store</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;UPDATE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">table</span><span class="si">}</span><span class="s1"> SET </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1">=? WHERE </span><span class="si">{</span><span class="n">owner</span><span class="o">.</span><span class="n">key</span><span class="si">}</span><span class="s1">=?;&#39;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">fetch</span><span class="p">,</span> <span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">])</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="n">conn</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">,</span> <span class="p">[</span><span class="n">value</span><span class="p">,</span> <span class="n">obj</span><span class="o">.</span><span class="n">key</span><span class="p">])</span>
<span class="n">conn</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
</div>
<p>We can use the <code class="xref py py-class docutils literal notranslate"><span class="pre">Field</span></code> class to define <a class="reference external" href="https://en.wikipedia.org/wiki/Database_model">models</a> that describe the schema for
each table in a database:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Movie</span><span class="p">:</span>
<span class="n">table</span> <span class="o">=</span> <span class="s1">&#39;Movies&#39;</span> <span class="c1"># Table name</span>
<span class="n">key</span> <span class="o">=</span> <span class="s1">&#39;title&#39;</span> <span class="c1"># Primary key</span>
<span class="n">director</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="n">year</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</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="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Song</span><span class="p">:</span>
<span class="n">table</span> <span class="o">=</span> <span class="s1">&#39;Music&#39;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s1">&#39;title&#39;</span>
<span class="n">artist</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="n">year</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="n">genre</span> <span class="o">=</span> <span class="n">Field</span><span class="p">()</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</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="bp">self</span><span class="o">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">key</span>
</pre></div>
</div>
<p>To use the models, first connect to the database:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span><span class="w"> </span><span class="nn">sqlite3</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s1">&#39;entertainment.db&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>An interactive session shows how data is retrieved from the database and how
it can be updated:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">Movie</span><span class="p">(</span><span class="s1">&#39;Star Wars&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">director</span>
<span class="go">&#39;George Lucas&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">jaws</span> <span class="o">=</span> <span class="n">Movie</span><span class="p">(</span><span class="s1">&#39;Jaws&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="sa">f</span><span class="s1">&#39;Released in </span><span class="si">{</span><span class="n">jaws</span><span class="o">.</span><span class="n">year</span><span class="si">}</span><span class="s1"> by </span><span class="si">{</span><span class="n">jaws</span><span class="o">.</span><span class="n">director</span><span class="si">}</span><span class="s1">&#39;</span>
<span class="go">&#39;Released in 1975 by Steven Spielberg&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Song</span><span class="p">(</span><span class="s1">&#39;Country Roads&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">artist</span>
<span class="go">&#39;John Denver&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Movie</span><span class="p">(</span><span class="s1">&#39;Star Wars&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">director</span> <span class="o">=</span> <span class="s1">&#39;J.J. Abrams&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">Movie</span><span class="p">(</span><span class="s1">&#39;Star Wars&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">director</span>
<span class="go">&#39;J.J. Abrams&#39;</span>
</pre></div>
</div>
</section>
</section>
<section id="pure-python-equivalents">
<h2><a class="toc-backref" href="#id23" role="doc-backlink">Pure Python Equivalents</a><a class="headerlink" href="#pure-python-equivalents" title="Link to this heading"></a></h2>
<p>The descriptor protocol is simple and offers exciting possibilities. Several
use cases are so common that they have been prepackaged into built-in tools.
Properties, bound methods, static methods, class methods, and __slots__ are
all based on the descriptor protocol.</p>
<section id="properties">
<h3><a class="toc-backref" href="#id24" role="doc-backlink">Properties</a><a class="headerlink" href="#properties" title="Link to this heading"></a></h3>
<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 a function call 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">-&gt;</span> <span class="nb">property</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-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">C</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </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="w"> </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="w"> </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">&quot;I&#39;m the &#39;x&#39; property.&quot;</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 that implements most of the core functionality:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Property</span><span class="p">:</span>
<span class="s2">&quot;Emulate PyProperty_Type() in Objects/descrobject.c&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="w"> </span><span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="vm">__name__</span> <span class="o">=</span> <span class="n">name</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="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="w"> </span><span class="fm">__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="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="w"> </span><span class="fm">__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="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="w"> </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="w"> </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="w"> </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-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Cell</span><span class="p">:</span>
<span class="o">...</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s2">&quot;Recalculate the cell before returning value&quot;</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>
</pre></div>
</div>
<p>Either the built-in <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> or our <code class="xref py py-func docutils literal notranslate"><span class="pre">Property()</span></code> equivalent would
work in this example.</p>
</section>
<section id="functions-and-methods">
<h3><a class="toc-backref" href="#id25" role="doc-backlink">Functions and methods</a><a class="headerlink" href="#functions-and-methods" title="Link to this heading"></a></h3>
<p>Pythons object oriented features are built upon a function based environment.
Using non-data descriptors, the two are merged seamlessly.</p>
<p>Functions stored in class dictionaries get turned into methods when invoked.
Methods only differ from regular functions in that the object instance is
prepended to the other arguments. By convention, the instance is called
<em>self</em> but could be called <em>this</em> or any other variable name.</p>
<p>Methods can be created manually with <a class="reference internal" href="../library/types.html#types.MethodType" title="types.MethodType"><code class="xref py py-class docutils literal notranslate"><span class="pre">types.MethodType</span></code></a> which is
roughly equivalent to:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">MethodType</span><span class="p">:</span>
<span class="s2">&quot;Emulate PyMethod_Type in Objects/classobject.c&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="vm">__func__</span> <span class="o">=</span> <span class="n">func</span>
<span class="bp">self</span><span class="o">.</span><span class="vm">__self__</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">func</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__func__</span>
<span class="n">obj</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__self__</span>
<span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__getattribute__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="s2">&quot;Emulate method_getset() in Objects/classobject.c&quot;</span>
<span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;__doc__&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__func__</span><span class="o">.</span><span class="vm">__doc__</span>
<span class="k">return</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">name</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__getattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="s2">&quot;Emulate method_getattro() in Objects/classobject.c&quot;</span>
<span class="k">return</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="vm">__func__</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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">&quot;Emulate method_descr_get() in Objects/classobject.c&quot;</span>
<span class="k">return</span> <span class="bp">self</span>
</pre></div>
</div>
<p>To support automatic creation of methods, 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 functions are non-data descriptors that return bound methods
during dotted lookup from an instance. Heres how it works:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Function</span><span class="p">:</span>
<span class="o">...</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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">&quot;Simulate func_descr_get() in Objects/funcobject.c&quot;</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">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 following class in the interpreter shows how the function
descriptor works in practice:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">D</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="nf">f</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="k">class</span><span class="w"> </span><span class="nc">D2</span><span class="p">:</span>
<span class="k">pass</span>
</pre></div>
</div>
<p>The function has a <a class="reference internal" href="../glossary.html#term-qualified-name"><span class="xref std std-term">qualified name</span></a> attribute to support introspection:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </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">&#39;D.f&#39;</span>
</pre></div>
</div>
<p>Accessing the function through the class dictionary does not invoke
<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>. Instead, it just returns the underlying function object:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">D</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="s1">&#39;f&#39;</span><span class="p">]</span>
<span class="go">&lt;function D.f at 0x00C45070&gt;</span>
</pre></div>
</div>
<p>Dotted access from a class calls <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> which just returns the
underlying function unchanged:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">D</span><span class="o">.</span><span class="n">f</span>
<span class="go">&lt;function D.f at 0x00C45070&gt;</span>
</pre></div>
</div>
<p>The interesting behavior occurs during dotted access from an instance. The
dotted lookup calls <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> which returns a bound method object:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">d</span> <span class="o">=</span> <span class="n">D</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span><span class="o">.</span><span class="n">f</span>
<span class="go">&lt;bound method D.f of &lt;__main__.D object at 0x00B18C90&gt;&gt;</span>
</pre></div>
</div>
<p>Internally, the bound method stores the underlying function and the bound
instance:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </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">&lt;function D.f at 0x00C45070&gt;</span>
<span class="gp">&gt;&gt;&gt; </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">&lt;__main__.D object at 0x00B18C90&gt;</span>
</pre></div>
</div>
<p>If you have ever wondered where <em>self</em> comes from in regular methods or where
<em>cls</em> comes from in class methods, this is it!</p>
</section>
<section id="kinds-of-methods">
<h3><a class="toc-backref" href="#id26" role="doc-backlink">Kinds of methods</a><a class="headerlink" href="#kinds-of-methods" title="Link to this heading"></a></h3>
<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">cls.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-default">
<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(cls, *args)</p></td>
</tr>
</tbody>
</table>
</div></blockquote>
</section>
<section id="static-methods">
<h3><a class="toc-backref" href="#id27" role="doc-backlink">Static methods</a><a class="headerlink" href="#static-methods" title="Link to this heading"></a></h3>
<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">&quot;f&quot;)</span></code> or <code class="docutils literal notranslate"><span class="pre">object.__getattribute__(C,</span> <span class="pre">&quot;f&quot;)</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">--&gt;</span> <span class="pre">0.9332</span></code>
or <code class="docutils literal notranslate"><span class="pre">Sample.erf(1.5)</span> <span class="pre">--&gt;</span> <span class="pre">0.9332</span></code>.</p>
<p>Since static methods return the underlying function with no changes, the
example calls are unexciting:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">E</span><span class="p">:</span>
<span class="nd">@staticmethod</span>
<span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span> <span class="o">*</span> <span class="mi">10</span>
</pre></div>
</div>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </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">30</span>
<span class="gp">&gt;&gt;&gt; </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">30</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-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">functools</span>
<span class="k">class</span><span class="w"> </span><span class="nc">StaticMethod</span><span class="p">:</span>
<span class="s2">&quot;Emulate PyStaticMethod_Type() in Objects/funcobject.c&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="n">functools</span><span class="o">.</span><span class="n">update_wrapper</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="k">def</span><span class="w"> </span><span class="fm">__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>
<span class="k">def</span><span class="w"> </span><span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</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="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">)</span>
</pre></div>
</div>
<p>The <a class="reference internal" href="../library/functools.html#functools.update_wrapper" title="functools.update_wrapper"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.update_wrapper()</span></code></a> call adds a <code class="docutils literal notranslate"><span class="pre">__wrapped__</span></code> attribute
that refers to the underlying function. Also it carries forward
the attributes necessary to make the wrapper look like the wrapped
function: <a class="reference internal" href="../reference/datamodel.html#function.__name__" title="function.__name__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__name__</span></code></a>, <a class="reference internal" href="../reference/datamodel.html#function.__qualname__" title="function.__qualname__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__qualname__</span></code></a>,
<a class="reference internal" href="../reference/datamodel.html#function.__doc__" title="function.__doc__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__doc__</span></code></a>, and <a class="reference internal" href="../reference/datamodel.html#function.__annotations__" title="function.__annotations__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__annotations__</span></code></a>.</p>
</section>
<section id="class-methods">
<h3><a class="toc-backref" href="#id28" role="doc-backlink">Class methods</a><a class="headerlink" href="#class-methods" title="Link to this heading"></a></h3>
<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 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">F</span><span class="p">:</span>
<span class="nd">@classmethod</span>
<span class="k">def</span><span class="w"> </span><span class="nf">f</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">x</span>
</pre></div>
</div>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">F</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">(&#39;F&#39;, 3)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">F</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">(&#39;F&#39;, 3)</span>
</pre></div>
</div>
<p>This behavior is useful whenever the method only needs to have a class
reference and does not rely on data stored in a specific instance. One use for
class methods is to create alternate class constructors. For example, 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-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Dict</span><span class="p">(</span><span class="nb">dict</span><span class="p">):</span>
<span class="nd">@classmethod</span>
<span class="k">def</span><span class="w"> </span><span class="nf">fromkeys</span><span class="p">(</span><span class="bp">cls</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">&quot;Emulate dict_fromkeys() in Objects/dictobject.c&quot;</span>
<span class="n">d</span> <span class="o">=</span> <span class="bp">cls</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>
</pre></div>
</div>
<p>Now a new dictionary of unique keys can be constructed like this:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">d</span> <span class="o">=</span> <span class="n">Dict</span><span class="o">.</span><span class="n">fromkeys</span><span class="p">(</span><span class="s1">&#39;abracadabra&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">type</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> <span class="ow">is</span> <span class="n">Dict</span>
<span class="go">True</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">d</span>
<span class="go">{&#39;a&#39;: None, &#39;b&#39;: None, &#39;r&#39;: None, &#39;c&#39;: None, &#39;d&#39;: 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-python notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">functools</span>
<span class="k">class</span><span class="w"> </span><span class="nc">ClassMethod</span><span class="p">:</span>
<span class="s2">&quot;Emulate PyClassMethod_Type() in Objects/funcobject.c&quot;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="n">functools</span><span class="o">.</span><span class="n">update_wrapper</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="k">def</span><span class="w"> </span><span class="fm">__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="bp">cls</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">cls</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">cls</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">return</span> <span class="n">MethodType</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">f</span><span class="p">,</span> <span class="bp">cls</span><span class="p">)</span>
</pre></div>
</div>
<p>The <a class="reference internal" href="../library/functools.html#functools.update_wrapper" title="functools.update_wrapper"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.update_wrapper()</span></code></a> call in <code class="docutils literal notranslate"><span class="pre">ClassMethod</span></code> adds a
<code class="docutils literal notranslate"><span class="pre">__wrapped__</span></code> attribute that refers to the underlying function. Also
it carries forward the attributes necessary to make the wrapper look
like the wrapped function: <a class="reference internal" href="../reference/datamodel.html#function.__name__" title="function.__name__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__name__</span></code></a>,
<a class="reference internal" href="../reference/datamodel.html#function.__qualname__" title="function.__qualname__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__qualname__</span></code></a>, <a class="reference internal" href="../reference/datamodel.html#function.__doc__" title="function.__doc__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__doc__</span></code></a>,
and <a class="reference internal" href="../reference/datamodel.html#function.__annotations__" title="function.__annotations__"><code class="xref py py-attr docutils literal notranslate"><span class="pre">__annotations__</span></code></a>.</p>
</section>
<section id="member-objects-and-slots">
<h3><a class="toc-backref" href="#id29" role="doc-backlink">Member objects and __slots__</a><a class="headerlink" href="#member-objects-and-slots" title="Link to this heading"></a></h3>
<p>When a class defines <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>, it replaces instance dictionaries with a
fixed-length array of slot values. From a user point of view that has
several effects:</p>
<p>1. Provides immediate detection of bugs due to misspelled attribute
assignments. Only attribute names specified in <code class="docutils literal notranslate"><span class="pre">__slots__</span></code> are allowed:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Vehicle</span><span class="p">:</span>
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;id_number&#39;</span><span class="p">,</span> <span class="s1">&#39;make&#39;</span><span class="p">,</span> <span class="s1">&#39;model&#39;</span><span class="p">)</span>
</pre></div>
</div>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">auto</span> <span class="o">=</span> <span class="n">Vehicle</span><span class="p">()</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">auto</span><span class="o">.</span><span class="n">id_nubmer</span> <span class="o">=</span> <span class="s1">&#39;VYE483814LQEX&#39;</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">&#39;Vehicle&#39; object has no attribute &#39;id_nubmer&#39;</span>
</pre></div>
</div>
<p>2. Helps create immutable objects where descriptors manage access to private
attributes stored in <code class="docutils literal notranslate"><span class="pre">__slots__</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Immutable</span><span class="p">:</span>
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;_dept&#39;</span><span class="p">,</span> <span class="s1">&#39;_name&#39;</span><span class="p">)</span> <span class="c1"># Replace the instance dictionary</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">dept</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_dept</span> <span class="o">=</span> <span class="n">dept</span> <span class="c1"># Store to private attribute</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="c1"># Store to private attribute</span>
<span class="nd">@property</span> <span class="c1"># Read-only descriptor</span>
<span class="k">def</span><span class="w"> </span><span class="nf">dept</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">_dept</span>
<span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="c1"># Read-only descriptor</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span>
</pre></div>
</div>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">mark</span> <span class="o">=</span> <span class="n">Immutable</span><span class="p">(</span><span class="s1">&#39;Botany&#39;</span><span class="p">,</span> <span class="s1">&#39;Mark Watney&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mark</span><span class="o">.</span><span class="n">dept</span>
<span class="go">&#39;Botany&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mark</span><span class="o">.</span><span class="n">dept</span> <span class="o">=</span> <span class="s1">&#39;Space Pirate&#39;</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">property &#39;dept&#39; of &#39;Immutable&#39; object has no setter</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">mark</span><span class="o">.</span><span class="n">location</span> <span class="o">=</span> <span class="s1">&#39;Mars&#39;</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">&#39;Immutable&#39; object has no attribute &#39;location&#39;</span>
</pre></div>
</div>
<p>3. Saves memory. On a 64-bit Linux build, an instance with two attributes
takes 48 bytes with <code class="docutils literal notranslate"><span class="pre">__slots__</span></code> and 152 bytes without. This <a class="reference external" href="https://en.wikipedia.org/wiki/Flyweight_pattern">flyweight
design pattern</a> likely only
matters when a large number of instances are going to be created.</p>
<p>4. Improves speed. Reading instance variables is 35% faster with
<code class="docutils literal notranslate"><span class="pre">__slots__</span></code> (as measured with Python 3.10 on an Apple M1 processor).</p>
<p>5. Blocks tools like <a class="reference internal" href="../library/functools.html#functools.cached_property" title="functools.cached_property"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.cached_property()</span></code></a> which require an
instance dictionary to function correctly:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">functools</span><span class="w"> </span><span class="kn">import</span> <span class="n">cached_property</span>
<span class="k">class</span><span class="w"> </span><span class="nc">CP</span><span class="p">:</span>
<span class="vm">__slots__</span> <span class="o">=</span> <span class="p">()</span> <span class="c1"># Eliminates the instance dict</span>
<span class="nd">@cached_property</span> <span class="c1"># Requires an instance dict</span>
<span class="k">def</span><span class="w"> </span><span class="nf">pi</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">4</span> <span class="o">*</span> <span class="nb">sum</span><span class="p">((</span><span class="o">-</span><span class="mf">1.0</span><span class="p">)</span><span class="o">**</span><span class="n">n</span> <span class="o">/</span> <span class="p">(</span><span class="mf">2.0</span><span class="o">*</span><span class="n">n</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">100_000</span><span class="p">)))</span>
</pre></div>
</div>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">CP</span><span class="p">()</span><span class="o">.</span><span class="n">pi</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="c">...</span>
<span class="gr">TypeError</span>: <span class="n">No &#39;__dict__&#39; attribute on &#39;CP&#39; instance to cache &#39;pi&#39; property.</span>
</pre></div>
</div>
<p>It is not possible to create an exact drop-in pure Python version of
<code class="docutils literal notranslate"><span class="pre">__slots__</span></code> because it requires direct access to C structures and control
over object memory allocation. However, we can build a mostly faithful
simulation where the actual C structure for slots is emulated by a private
<code class="docutils literal notranslate"><span class="pre">_slotvalues</span></code> list. Reads and writes to that private structure are managed
by member descriptors:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">null</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span>
<span class="k">class</span><span class="w"> </span><span class="nc">Member</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">clsname</span><span class="p">,</span> <span class="n">offset</span><span class="p">):</span>
<span class="s1">&#39;Emulate PyMemberDef in Include/structmember.h&#39;</span>
<span class="c1"># Also see descr_new() in Objects/descrobject.c</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="bp">self</span><span class="o">.</span><span class="n">clsname</span> <span class="o">=</span> <span class="n">clsname</span>
<span class="bp">self</span><span class="o">.</span><span class="n">offset</span> <span class="o">=</span> <span class="n">offset</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="s1">&#39;Emulate member_get() in Objects/descrobject.c&#39;</span>
<span class="c1"># Also see PyMember_GetOne() in Python/structmember.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="n">value</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">_slotvalues</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">offset</span><span class="p">]</span>
<span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="n">null</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</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="n">value</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="s1">&#39;Emulate member_set() in Objects/descrobject.c&#39;</span>
<span class="n">obj</span><span class="o">.</span><span class="n">_slotvalues</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">offset</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__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="s1">&#39;Emulate member_delete() in Objects/descrobject.c&#39;</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">_slotvalues</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">offset</span><span class="p">]</span>
<span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="n">null</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</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="n">obj</span><span class="o">.</span><span class="n">_slotvalues</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">offset</span><span class="p">]</span> <span class="o">=</span> <span class="n">null</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="s1">&#39;Emulate member_repr() in Objects/descrobject.c&#39;</span>
<span class="k">return</span> <span class="sa">f</span><span class="s1">&#39;&lt;Member </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">!r}</span><span class="s1"> of </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">clsname</span><span class="si">!r}</span><span class="s1">&gt;&#39;</span>
</pre></div>
</div>
<p>The <code class="xref py py-meth docutils literal notranslate"><span class="pre">type.__new__()</span></code> method takes care of adding member objects to class
variables:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Type</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
<span class="s1">&#39;Simulate how the type metaclass adds member objects for slots&#39;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__new__</span><span class="p">(</span><span class="n">mcls</span><span class="p">,</span> <span class="n">clsname</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">mapping</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="s1">&#39;Emulate type_new() in Objects/typeobject.c&#39;</span>
<span class="c1"># type_new() calls PyTypeReady() which calls add_methods()</span>
<span class="n">slot_names</span> <span class="o">=</span> <span class="n">mapping</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;slot_names&#39;</span><span class="p">,</span> <span class="p">[])</span>
<span class="k">for</span> <span class="n">offset</span><span class="p">,</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">slot_names</span><span class="p">):</span>
<span class="n">mapping</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="n">Member</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">clsname</span><span class="p">,</span> <span class="n">offset</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">type</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="n">mcls</span><span class="p">,</span> <span class="n">clsname</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">mapping</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
</div>
<p>The <a class="reference internal" href="../reference/datamodel.html#object.__new__" title="object.__new__"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__new__()</span></code></a> method takes care of creating instances that have
slots instead of an instance dictionary. Here is a rough simulation in pure
Python:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Object</span><span class="p">:</span>
<span class="s1">&#39;Simulate how object.__new__() allocates memory for __slots__&#39;</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="s1">&#39;Emulate object_new() in Objects/typeobject.c&#39;</span>
<span class="n">inst</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;slot_names&#39;</span><span class="p">):</span>
<span class="n">empty_slots</span> <span class="o">=</span> <span class="p">[</span><span class="n">null</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="bp">cls</span><span class="o">.</span><span class="n">slot_names</span><span class="p">)</span>
<span class="nb">object</span><span class="o">.</span><span class="fm">__setattr__</span><span class="p">(</span><span class="n">inst</span><span class="p">,</span> <span class="s1">&#39;_slotvalues&#39;</span><span class="p">,</span> <span class="n">empty_slots</span><span class="p">)</span>
<span class="k">return</span> <span class="n">inst</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__setattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="s1">&#39;Emulate _PyObject_GenericSetAttrWithDict() Objects/object.c&#39;</span>
<span class="bp">cls</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;slot_names&#39;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">slot_names</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s1"> object has no attribute </span><span class="si">{</span><span class="n">name</span><span class="si">!r}</span><span class="s1">&#39;</span>
<span class="p">)</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__setattr__</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__delattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="s1">&#39;Emulate _PyObject_GenericSetAttrWithDict() Objects/object.c&#39;</span>
<span class="bp">cls</span> <span class="o">=</span> <span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;slot_names&#39;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">name</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">cls</span><span class="o">.</span><span class="n">slot_names</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">(</span>
<span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">!r}</span><span class="s1"> object has no attribute </span><span class="si">{</span><span class="n">name</span><span class="si">!r}</span><span class="s1">&#39;</span>
<span class="p">)</span>
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__delattr__</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre></div>
</div>
<p>To use the simulation in a real class, just inherit from <code class="xref py py-class docutils literal notranslate"><span class="pre">Object</span></code> and
set the <a class="reference internal" href="../glossary.html#term-metaclass"><span class="xref std std-term">metaclass</span></a> to <code class="xref py py-class docutils literal notranslate"><span class="pre">Type</span></code>:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">H</span><span class="p">(</span><span class="n">Object</span><span class="p">,</span> <span class="n">metaclass</span><span class="o">=</span><span class="n">Type</span><span class="p">):</span>
<span class="s1">&#39;Instance variables stored in slots&#39;</span>
<span class="n">slot_names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;x&#39;</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">]</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</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="n">y</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">x</span>
<span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">y</span>
</pre></div>
</div>
<p>At this point, the metaclass has loaded member objects for <em>x</em> and <em>y</em>:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span><span class="w"> </span><span class="nn">pprint</span><span class="w"> </span><span class="kn">import</span> <span class="n">pp</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">pp</span><span class="p">(</span><span class="nb">dict</span><span class="p">(</span><span class="nb">vars</span><span class="p">(</span><span class="n">H</span><span class="p">)))</span>
<span class="go">{&#39;__module__&#39;: &#39;__main__&#39;,</span>
<span class="go"> &#39;__doc__&#39;: &#39;Instance variables stored in slots&#39;,</span>
<span class="go"> &#39;slot_names&#39;: [&#39;x&#39;, &#39;y&#39;],</span>
<span class="go"> &#39;__init__&#39;: &lt;function H.__init__ at 0x7fb5d302f9d0&gt;,</span>
<span class="go"> &#39;x&#39;: &lt;Member &#39;x&#39; of &#39;H&#39;&gt;,</span>
<span class="go"> &#39;y&#39;: &lt;Member &#39;y&#39; of &#39;H&#39;&gt;}</span>
</pre></div>
</div>
<p>When instances are created, they have a <code class="docutils literal notranslate"><span class="pre">slot_values</span></code> list where the
attributes are stored:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">h</span> <span class="o">=</span> <span class="n">H</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="go">{&#39;_slotvalues&#39;: [10, 20]}</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">h</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">55</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">vars</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="go">{&#39;_slotvalues&#39;: [55, 20]}</span>
</pre></div>
</div>
<p>Misspelled or unassigned attributes will raise an exception:</p>
<div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="n">h</span><span class="o">.</span><span class="n">xz</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="w"> </span><span class="o">...</span>
<span class="gr">AttributeError</span>: <span class="n">&#39;H&#39; object has no attribute &#39;xz&#39;</span>
</pre></div>
</div>
</section>
</section>
</section>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="Main">
<div class="sphinxsidebarwrapper">
<div>
<h3><a href="../contents.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Descriptor Guide</a><ul>
<li><a class="reference internal" href="#primer">Primer</a><ul>
<li><a class="reference internal" href="#simple-example-a-descriptor-that-returns-a-constant">Simple example: A descriptor that returns a constant</a></li>
<li><a class="reference internal" href="#dynamic-lookups">Dynamic lookups</a></li>
<li><a class="reference internal" href="#managed-attributes">Managed attributes</a></li>
<li><a class="reference internal" href="#customized-names">Customized names</a></li>
<li><a class="reference internal" href="#closing-thoughts">Closing thoughts</a></li>
</ul>
</li>
<li><a class="reference internal" href="#complete-practical-example">Complete Practical Example</a><ul>
<li><a class="reference internal" href="#validator-class">Validator class</a></li>
<li><a class="reference internal" href="#custom-validators">Custom validators</a></li>
<li><a class="reference internal" href="#practical-application">Practical application</a></li>
</ul>
</li>
<li><a class="reference internal" href="#technical-tutorial">Technical Tutorial</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="#overview-of-descriptor-invocation">Overview of descriptor invocation</a></li>
<li><a class="reference internal" href="#invocation-from-an-instance">Invocation from an instance</a></li>
<li><a class="reference internal" href="#invocation-from-a-class">Invocation from a class</a></li>
<li><a class="reference internal" href="#invocation-from-super">Invocation from super</a></li>
<li><a class="reference internal" href="#summary-of-invocation-logic">Summary of invocation logic</a></li>
<li><a class="reference internal" href="#automatic-name-notification">Automatic name notification</a></li>
<li><a class="reference internal" href="#orm-example">ORM example</a></li>
</ul>
</li>
<li><a class="reference internal" href="#pure-python-equivalents">Pure Python Equivalents</a><ul>
<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="#kinds-of-methods">Kinds of methods</a></li>
<li><a class="reference internal" href="#static-methods">Static methods</a></li>
<li><a class="reference internal" href="#class-methods">Class methods</a></li>
<li><a class="reference internal" href="#member-objects-and-slots">Member objects and __slots__</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<div>
<h4>Previous topic</h4>
<p class="topless"><a href="curses.html"
title="previous chapter">Curses Programming with Python</a></p>
</div>
<div>
<h4>Next topic</h4>
<p class="topless"><a href="gdb_helpers.html"
title="next chapter">Debugging C API extensions and CPython Internals with GDB</a></p>
</div>
<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/main/Doc/howto/descriptor.rst"
rel="nofollow">Show Source
</a>
</li>
</ul>
</div>
</div>
<div id="sidebarbutton" title="Collapse sidebar">
<span>«</span>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="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="gdb_helpers.html" title="Debugging C API extensions and CPython Internals with GDB"
>next</a> |</li>
<li class="right" >
<a href="curses.html" title="Curses Programming with Python"
>previous</a> |</li>
<li><img src="../_static/py.svg" alt="Python logo" style="vertical-align: middle; margin-top: -1px"/></li>
<li><a href="https://www.python.org/">Python</a> &#187;</li>
<li class="switchers">
<div class="language_switcher_placeholder"></div>
<div class="version_switcher_placeholder"></div>
</li>
<li>
</li>
<li id="cpython-language-and-version">
<a href="../index.html">3.13.3 Documentation</a> &#187;
</li>
<li class="nav-item nav-item-1"><a href="index.html" >Python HOWTOs</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Descriptor Guide</a></li>
<li class="right">
<div class="inline-search" role="search">
<form class="inline-search" action="../search.html" method="get">
<input placeholder="Quick search" aria-label="Quick search" type="search" name="q" id="search-box" />
<input type="submit" value="Go" />
</form>
</div>
|
</li>
<li class="right">
<label class="theme-selector-label">
Theme
<select class="theme-selector" oninput="activateTheme(this.value)">
<option value="auto" selected>Auto</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label> |</li>
</ul>
</div>
<div class="footer">
&copy;
<a href="../copyright.html">
Copyright
</a>
2001-2025, Python Software Foundation.
<br />
This page is licensed under the Python Software Foundation License Version 2.
<br />
Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License.
<br />
See <a href="/license.html">History and License</a> for more information.<br />
<br />
The Python Software Foundation is a non-profit corporation.
<a href="https://www.python.org/psf/donations/">Please donate.</a>
<br />
<br />
Last updated on Apr 08, 2025 (14:33 UTC).
<a href="/bugs.html">Found a bug</a>?
<br />
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.2.3.
</div>
</body>
</html>