python-peps/pep-0231/index.html

752 lines
74 KiB
HTML
Raw Permalink 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">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 231 __findattr__() | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0231/">
<link rel="stylesheet" href="../_static/style.css" type="text/css">
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
<meta property="og:title" content='PEP 231 __findattr__() | peps.python.org'>
<meta property="og:description" content="This PEP describes an extension to instance attribute lookup and modification machinery, which allows pure-Python implementations of many interesting programming models. This PEP tracks the status and ownership of this feature. It contains a descripti...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0231/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="This PEP describes an extension to instance attribute lookup and modification machinery, which allows pure-Python implementations of many interesting programming models. This PEP tracks the status and ownership of this feature. It contains a descripti...">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 231</li>
</ul>
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
</button>
</header>
<article>
<section id="pep-content">
<h1 class="page-title">PEP 231 __findattr__()</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Barry Warsaw &lt;barry&#32;&#97;t&#32;python.org&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</abbr></dd>
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-even">Created<span class="colon">:</span></dt>
<dd class="field-even">30-Nov-2000</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">2.1</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even"><p></p></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#introduction">Introduction</a></li>
<li><a class="reference internal" href="#background">Background</a></li>
<li><a class="reference internal" href="#proposal">Proposal</a></li>
<li><a class="reference internal" href="#key-differences-with-the-existing-protocol">Key Differences with the Existing Protocol</a></li>
<li><a class="reference internal" href="#related-work">Related Work</a></li>
<li><a class="reference internal" href="#examples">Examples</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#rejection">Rejection</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<section id="introduction">
<h2><a class="toc-backref" href="#introduction" role="doc-backlink">Introduction</a></h2>
<p>This PEP describes an extension to instance attribute lookup and
modification machinery, which allows pure-Python implementations
of many interesting programming models. This PEP tracks the
status and ownership of this feature. It contains a description
of the feature and outlines changes necessary to support the
feature. This PEP summarizes discussions held in mailing list
forums, and provides URLs for further information, where
appropriate. The CVS revision history of this file contains the
definitive historical record.</p>
</section>
<section id="background">
<h2><a class="toc-backref" href="#background" role="doc-backlink">Background</a></h2>
<p>The semantics for Python instances allow the programmer to
customize some aspects of attribute lookup and attribute
modification, through the special methods <code class="docutils literal notranslate"><span class="pre">__getattr__()</span></code> and
<code class="docutils literal notranslate"><span class="pre">__setattr__()</span></code> <a class="footnote-reference brackets" href="#id7" id="id1">[1]</a>.</p>
<p>However, because of certain restrictions imposed by these methods,
there are useful programming techniques that can not be written in
Python alone, e.g. strict Java Bean-like <a class="footnote-reference brackets" href="#id8" id="id2">[2]</a> interfaces and Zope
style acquisitions <a class="footnote-reference brackets" href="#id9" id="id3">[3]</a>. In the latter case, Zope solves this by
including a C extension called ExtensionClass <a class="footnote-reference brackets" href="#id10" id="id4">[5]</a> which modifies
the standard class semantics, and uses a metaclass hook in
Pythons class model called alternatively the “Don Beaudry Hook”
or “Don Beaudry Hack” <a class="footnote-reference brackets" href="#id11" id="id5">[6]</a>.</p>
<p>While Zopes approach works, it has several disadvantages. First,
it requires a C extension. Second it employs a very arcane, but
truck-sized loophole in the Python machinery. Third, it can be
difficult for other programmers to use and understand (the
metaclass has well-known brain exploding properties). And fourth,
because ExtensionClass instances arent “real” Python instances,
some aspects of the Python runtime system dont work with
ExtensionClass instances.</p>
<p>Proposals for fixing this problem have often been lumped under the
rubric of fixing the “class/type dichotomy”; that is, eliminating
the difference between built-in types and classes <a class="footnote-reference brackets" href="#id12" id="id6">[7]</a>. While a
laudable goal itself, repairing this rift is not necessary in
order to achieve the types of programming constructs described
above. This proposal provides an 80% solution with a minimum of
modification to Pythons class and instance objects. It does
nothing to address the type/class dichotomy.</p>
</section>
<section id="proposal">
<h2><a class="toc-backref" href="#proposal" role="doc-backlink">Proposal</a></h2>
<p>This proposal adds a new special method called <code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code> with
the following semantics:</p>
<ul class="simple">
<li>If defined in a class, it will be called on all instance
attribute resolutions instead of <code class="docutils literal notranslate"><span class="pre">__getattr__()</span></code> and
<code class="docutils literal notranslate"><span class="pre">__setattr__()</span></code>.</li>
<li><code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code> is never called recursively. That is, when a
specific instances <code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code> is on the call stack, further
attribute accesses for that instance will use the standard
<code class="docutils literal notranslate"><span class="pre">__getattr__()</span></code> and <code class="docutils literal notranslate"><span class="pre">__setattr__()</span></code> methods.</li>
<li><code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code> is called for both attribute access (getting)
and attribute modification (setting). It is not called for
attribute deletion.</li>
<li>When called for getting, it is passed a single argument (not
counting self): the name of the attribute being accessed.</li>
<li>When called for setting, it is called with third argument, which
is the value to set the attribute to.</li>
<li><code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code> methods have the same caching semantics as
<code class="docutils literal notranslate"><span class="pre">__getattr__()</span></code> and <code class="docutils literal notranslate"><span class="pre">__setattr__()</span></code>; i.e. if they are present in the
class at class definition time, they are used, but if they are
subsequently added to a class later they are not.</li>
</ul>
</section>
<section id="key-differences-with-the-existing-protocol">
<h2><a class="toc-backref" href="#key-differences-with-the-existing-protocol" role="doc-backlink">Key Differences with the Existing Protocol</a></h2>
<p><code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code>s semantics are different from the existing
protocol in key ways:</p>
<p>First, <code class="docutils literal notranslate"><span class="pre">__getattr__()</span></code> is never called if the attribute is found in
the instances <code class="docutils literal notranslate"><span class="pre">__dict__</span></code>. This is done for efficiency reasons, and
because otherwise, <code class="docutils literal notranslate"><span class="pre">__setattr__()</span></code> would have no way to get to the
instances attributes.</p>
<p>Second, <code class="docutils literal notranslate"><span class="pre">__setattr__()</span></code> cannot use “normal” syntax for setting
instance attributes, e.g. “self.name = foo” because that would
cause recursive calls to <code class="docutils literal notranslate"><span class="pre">__setattr__()</span></code>.</p>
<p><code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code> is always called regardless of whether the
attribute is in <code class="docutils literal notranslate"><span class="pre">__dict__</span></code> or not, and a flag in the instance object
prevents recursive calls to <code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code>. This gives the class
a chance to perform some action for every attribute access. And
because it is called for both gets and sets, it is easy to write
similar policy for all attribute access. Further, efficiency is
not a problem because it is only paid when the extended mechanism
is used.</p>
</section>
<section id="related-work">
<h2><a class="toc-backref" href="#related-work" role="doc-backlink">Related Work</a></h2>
<p><a class="pep reference internal" href="../pep-0213/" title="PEP 213 Attribute Access Handlers">PEP 213</a> describes a different approach to hooking into
attribute access and modification. The semantics proposed in <a class="pep reference internal" href="../pep-0213/" title="PEP 213 Attribute Access Handlers">PEP 213</a>
can be implemented using the <code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code> hook described
here, with one caveat. The current reference implementation of
<code class="docutils literal notranslate"><span class="pre">__findattr__()</span></code> does not support hooking on attribute deletion.
This could be added if its found desirable. See example below.</p>
</section>
<section id="examples">
<h2><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h2>
<p>One programming style that this proposal allows is a Java
Bean-like interface to objects, where unadorned attribute access
and modification is transparently mapped to a functional
interface. E.g.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Bean</span><span class="p">:</span>
<span class="k">def</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="bp">self</span><span class="o">.</span><span class="n">__myfoo</span> <span class="o">=</span> <span class="n">x</span>
<span class="k">def</span> <span class="nf">__findattr__</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="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="k">if</span> <span class="n">name</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;_&#39;</span><span class="p">):</span>
<span class="c1"># Private names</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span> <span class="nb">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">args</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">else</span><span class="p">:</span> <span class="k">return</span> <span class="nb">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="k">else</span><span class="p">:</span>
<span class="c1"># Public names</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span> <span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;_set_&#39;</span> <span class="o">+</span> <span class="n">name</span>
<span class="k">else</span><span class="p">:</span> <span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;_get_&#39;</span> <span class="o">+</span> <span class="n">name</span>
<span class="k">return</span> <span class="nb">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="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_set_foo</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="bp">self</span><span class="o">.</span><span class="n">__myfoo</span> <span class="o">=</span> <span class="n">x</span>
<span class="k">def</span> <span class="nf">_get_foo</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">__myfoo</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">Bean</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">b</span><span class="o">.</span><span class="n">foo</span>
<span class="n">b</span><span class="o">.</span><span class="n">foo</span> <span class="o">=</span> <span class="mi">9</span>
<span class="nb">print</span> <span class="n">b</span><span class="o">.</span><span class="n">foo</span>
</pre></div>
</div>
<p>A second, more elaborate example is the implementation of both
implicit and explicit acquisition in pure Python:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">types</span>
<span class="k">class</span> <span class="nc">MethodWrapper</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">container</span><span class="p">,</span> <span class="n">method</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__container</span> <span class="o">=</span> <span class="n">container</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__method</span> <span class="o">=</span> <span class="n">method</span>
<span class="k">def</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">kws</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__method</span><span class="o">.</span><span class="n">im_func</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__container</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">kws</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">WrapperImplicit</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">contained</span><span class="p">,</span> <span class="n">container</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__contained</span> <span class="o">=</span> <span class="n">contained</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__container</span> <span class="o">=</span> <span class="n">container</span>
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s1">&#39;&lt;Wrapper: [</span><span class="si">%s</span><span class="s1"> | </span><span class="si">%s</span><span class="s1">]&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__container</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__contained</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__findattr__</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="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="c1"># Some things are our own</span>
<span class="k">if</span> <span class="n">name</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;_WrapperImplicit__&#39;</span><span class="p">):</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span> <span class="k">return</span> <span class="nb">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="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span> <span class="k">return</span> <span class="nb">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="c1"># setattr stores the name on the contained object directly</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__contained</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="c1"># Other special names</span>
<span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;aq_parent&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__container</span>
<span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;aq_self&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__contained</span>
<span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;aq_base&#39;</span><span class="p">:</span>
<span class="n">base</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__contained</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">base</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">aq_self</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">return</span> <span class="n">base</span>
<span class="c1"># no acquisition for _ names</span>
<span class="k">if</span> <span class="n">name</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;_&#39;</span><span class="p">):</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="n">__contained</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
<span class="c1"># Everything else gets wrapped</span>
<span class="n">missing</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">which</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__contained</span>
<span class="n">obj</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">which</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">missing</span><span class="p">)</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="n">missing</span><span class="p">:</span>
<span class="n">which</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__container</span>
<span class="n">obj</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">which</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">missing</span><span class="p">)</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="n">missing</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">,</span> <span class="n">name</span>
<span class="n">of</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="s1">&#39;__of__&#39;</span><span class="p">,</span> <span class="n">missing</span><span class="p">)</span>
<span class="k">if</span> <span class="n">of</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">missing</span><span class="p">:</span>
<span class="k">return</span> <span class="n">of</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">elif</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="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">:</span>
<span class="k">return</span> <span class="n">MethodWrapper</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">return</span> <span class="n">obj</span>
<span class="k">class</span> <span class="nc">WrapperExplicit</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">contained</span><span class="p">,</span> <span class="n">container</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__contained</span> <span class="o">=</span> <span class="n">contained</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__container</span> <span class="o">=</span> <span class="n">container</span>
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="s1">&#39;&lt;Wrapper: [</span><span class="si">%s</span><span class="s1"> | </span><span class="si">%s</span><span class="s1">]&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__container</span><span class="p">,</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__contained</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__findattr__</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="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="c1"># Some things are our own</span>
<span class="k">if</span> <span class="n">name</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s1">&#39;_WrapperExplicit__&#39;</span><span class="p">):</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span> <span class="k">return</span> <span class="nb">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="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span> <span class="k">return</span> <span class="nb">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="c1"># setattr stores the name on the contained object directly</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__contained</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="c1"># Other special names</span>
<span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;aq_parent&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__container</span>
<span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;aq_self&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__contained</span>
<span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;aq_base&#39;</span><span class="p">:</span>
<span class="n">base</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__contained</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">while</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">base</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">aq_self</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">return</span> <span class="n">base</span>
<span class="k">elif</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;aq_acquire&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">aq_acquire</span>
<span class="c1"># explicit acquisition only</span>
<span class="n">obj</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__contained</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
<span class="k">if</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="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">:</span>
<span class="k">return</span> <span class="n">MethodWrapper</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">return</span> <span class="n">obj</span>
<span class="k">def</span> <span class="nf">aq_acquire</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="c1"># Everything else gets wrapped</span>
<span class="n">missing</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">which</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__contained</span>
<span class="n">obj</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">which</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">missing</span><span class="p">)</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="n">missing</span><span class="p">:</span>
<span class="n">which</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__container</span>
<span class="n">obj</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">which</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">missing</span><span class="p">)</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="n">missing</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">AttributeError</span><span class="p">,</span> <span class="n">name</span>
<span class="n">of</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="s1">&#39;__of__&#39;</span><span class="p">,</span> <span class="n">missing</span><span class="p">)</span>
<span class="k">if</span> <span class="n">of</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">missing</span><span class="p">:</span>
<span class="k">return</span> <span class="n">of</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">elif</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="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">:</span>
<span class="k">return</span> <span class="n">MethodWrapper</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">return</span> <span class="n">obj</span>
<span class="k">class</span> <span class="nc">Implicit</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__of__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">container</span><span class="p">):</span>
<span class="k">return</span> <span class="n">WrapperImplicit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">container</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__findattr__</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="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="c1"># ignore setattrs</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">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">args</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">obj</span> <span class="o">=</span> <span class="nb">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="n">missing</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">of</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="s1">&#39;__of__&#39;</span><span class="p">,</span> <span class="n">missing</span><span class="p">)</span>
<span class="k">if</span> <span class="n">of</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">missing</span><span class="p">:</span>
<span class="k">return</span> <span class="n">of</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="k">return</span> <span class="n">obj</span>
<span class="k">class</span> <span class="nc">Explicit</span><span class="p">(</span><span class="n">Implicit</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">__of__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">container</span><span class="p">):</span>
<span class="k">return</span> <span class="n">WrapperExplicit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">container</span><span class="p">)</span>
<span class="c1"># tests</span>
<span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="n">Implicit</span><span class="p">):</span>
<span class="n">color</span> <span class="o">=</span> <span class="s1">&#39;red&#39;</span>
<span class="k">class</span> <span class="nc">A</span><span class="p">(</span><span class="n">Implicit</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">report</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">color</span>
<span class="c1"># simple implicit acquisition</span>
<span class="n">c</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">()</span>
<span class="n">c</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">a</span>
<span class="k">assert</span> <span class="n">c</span><span class="o">.</span><span class="n">a</span><span class="o">.</span><span class="n">report</span><span class="p">()</span> <span class="o">==</span> <span class="s1">&#39;red&#39;</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">C</span><span class="p">()</span>
<span class="n">d</span><span class="o">.</span><span class="n">color</span> <span class="o">=</span> <span class="s1">&#39;green&#39;</span>
<span class="n">d</span><span class="o">.</span><span class="n">a</span> <span class="o">=</span> <span class="n">a</span>
<span class="k">assert</span> <span class="n">d</span><span class="o">.</span><span class="n">a</span><span class="o">.</span><span class="n">report</span><span class="p">()</span> <span class="o">==</span> <span class="s1">&#39;green&#39;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">a</span><span class="o">.</span><span class="n">report</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;AttributeError expected&#39;</span>
<span class="c1"># special names</span>
<span class="k">assert</span> <span class="n">c</span><span class="o">.</span><span class="n">a</span><span class="o">.</span><span class="n">aq_parent</span> <span class="ow">is</span> <span class="n">c</span>
<span class="k">assert</span> <span class="n">c</span><span class="o">.</span><span class="n">a</span><span class="o">.</span><span class="n">aq_self</span> <span class="ow">is</span> <span class="n">a</span>
<span class="n">c</span><span class="o">.</span><span class="n">a</span><span class="o">.</span><span class="n">d</span> <span class="o">=</span> <span class="n">d</span>
<span class="k">assert</span> <span class="n">c</span><span class="o">.</span><span class="n">a</span><span class="o">.</span><span class="n">d</span><span class="o">.</span><span class="n">aq_base</span> <span class="ow">is</span> <span class="n">d</span>
<span class="k">assert</span> <span class="n">c</span><span class="o">.</span><span class="n">a</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">a</span>
<span class="c1"># no acquisition on _ names</span>
<span class="k">class</span> <span class="nc">E</span><span class="p">(</span><span class="n">Implicit</span><span class="p">):</span>
<span class="n">_color</span> <span class="o">=</span> <span class="s1">&#39;purple&#39;</span>
<span class="k">class</span> <span class="nc">F</span><span class="p">(</span><span class="n">Implicit</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">report</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">_color</span>
<span class="n">e</span> <span class="o">=</span> <span class="n">E</span><span class="p">()</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">F</span><span class="p">()</span>
<span class="n">e</span><span class="o">.</span><span class="n">f</span> <span class="o">=</span> <span class="n">f</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">e</span><span class="o">.</span><span class="n">f</span><span class="o">.</span><span class="n">report</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;AttributeError expected&#39;</span>
<span class="c1"># explicit</span>
<span class="k">class</span> <span class="nc">G</span><span class="p">(</span><span class="n">Explicit</span><span class="p">):</span>
<span class="n">color</span> <span class="o">=</span> <span class="s1">&#39;pink&#39;</span>
<span class="k">class</span> <span class="nc">H</span><span class="p">(</span><span class="n">Explicit</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">report</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">aq_acquire</span><span class="p">(</span><span class="s1">&#39;color&#39;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">barf</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">color</span>
<span class="n">g</span> <span class="o">=</span> <span class="n">G</span><span class="p">()</span>
<span class="n">h</span> <span class="o">=</span> <span class="n">H</span><span class="p">()</span>
<span class="n">g</span><span class="o">.</span><span class="n">h</span> <span class="o">=</span> <span class="n">h</span>
<span class="k">assert</span> <span class="n">g</span><span class="o">.</span><span class="n">h</span><span class="o">.</span><span class="n">report</span><span class="p">()</span> <span class="o">==</span> <span class="s1">&#39;pink&#39;</span>
<span class="n">i</span> <span class="o">=</span> <span class="n">G</span><span class="p">()</span>
<span class="n">i</span><span class="o">.</span><span class="n">color</span> <span class="o">=</span> <span class="s1">&#39;cyan&#39;</span>
<span class="n">i</span><span class="o">.</span><span class="n">h</span> <span class="o">=</span> <span class="n">h</span>
<span class="k">assert</span> <span class="n">i</span><span class="o">.</span><span class="n">h</span><span class="o">.</span><span class="n">report</span><span class="p">()</span> <span class="o">==</span> <span class="s1">&#39;cyan&#39;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">g</span><span class="o">.</span><span class="n">i</span><span class="o">.</span><span class="n">barf</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;AttributeError expected&#39;</span>
</pre></div>
</div>
<p>C++-like access control can also be accomplished, although less
cleanly because of the difficulty of figuring out what method is
being called from the runtime call stack:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">types</span>
<span class="n">PUBLIC</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">PROTECTED</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">PRIVATE</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">getframe</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">_getframe</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">getframe</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span> <span class="k">raise</span> <span class="ne">Exception</span>
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
<span class="n">frame</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()[</span><span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="n">tb_frame</span>
<span class="k">while</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">frame</span> <span class="o">=</span> <span class="n">frame</span><span class="o">.</span><span class="n">f_back</span>
<span class="k">if</span> <span class="n">frame</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">,</span> <span class="s1">&#39;call stack is not deep enough&#39;</span>
<span class="k">return</span> <span class="n">frame</span>
<span class="k">class</span> <span class="nc">AccessViolation</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
<span class="k">pass</span>
<span class="k">class</span> <span class="nc">Access</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__findattr__</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="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="n">methcache</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__dict__</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s1">&#39;__cache__&#39;</span><span class="p">,</span> <span class="p">{})</span>
<span class="n">missing</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">obj</span> <span class="o">=</span> <span class="nb">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="n">missing</span><span class="p">)</span>
<span class="c1"># if obj is missing we better be doing a setattr for</span>
<span class="c1"># the first time</span>
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">missing</span> <span class="ow">and</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="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">:</span>
<span class="c1"># Digusting hack because there&#39;s no way to</span>
<span class="c1"># dynamically figure out what the method being</span>
<span class="c1"># called is from the stack frame.</span>
<span class="n">methcache</span><span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">im_func</span><span class="o">.</span><span class="n">func_code</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">im_class</span>
<span class="c1">#</span>
<span class="c1"># What&#39;s the access permissions for this name?</span>
<span class="n">access</span><span class="p">,</span> <span class="n">klass</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s1">&#39;__access__&#39;</span><span class="p">,</span> <span class="p">{})</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
<span class="n">name</span><span class="p">,</span> <span class="p">(</span><span class="n">PUBLIC</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span>
<span class="k">if</span> <span class="n">access</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">PUBLIC</span><span class="p">:</span>
<span class="c1"># Now try to see which method is calling us</span>
<span class="n">frame</span> <span class="o">=</span> <span class="n">getframe</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">f_back</span>
<span class="k">if</span> <span class="n">frame</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">AccessViolation</span>
<span class="c1"># Get the class of the method that&#39;s accessing</span>
<span class="c1"># this attribute, by using the code object cache</span>
<span class="k">if</span> <span class="n">frame</span><span class="o">.</span><span class="n">f_code</span><span class="o">.</span><span class="n">co_name</span> <span class="o">==</span> <span class="s1">&#39;__init__&#39;</span><span class="p">:</span>
<span class="c1"># There aren&#39;t entries in the cache for ctors,</span>
<span class="c1"># because the calling mechanism doesn&#39;t go</span>
<span class="c1"># through __findattr__(). Are there other</span>
<span class="c1"># methods that might have the same behavior?</span>
<span class="c1"># Since we can&#39;t know who&#39;s __init__ we&#39;re in,</span>
<span class="c1"># for now we&#39;ll assume that only protected and</span>
<span class="c1"># public attrs can be accessed.</span>
<span class="k">if</span> <span class="n">access</span> <span class="ow">is</span> <span class="n">PRIVATE</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">AccessViolation</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">methclass</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__cache__</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">frame</span><span class="o">.</span><span class="n">f_code</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">methclass</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">AccessViolation</span>
<span class="k">if</span> <span class="n">access</span> <span class="ow">is</span> <span class="n">PRIVATE</span> <span class="ow">and</span> <span class="n">methclass</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">klass</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">AccessViolation</span>
<span class="k">if</span> <span class="n">access</span> <span class="ow">is</span> <span class="n">PROTECTED</span> <span class="ow">and</span> <span class="ow">not</span> <span class="nb">issubclass</span><span class="p">(</span><span class="n">methclass</span><span class="p">,</span>
<span class="n">klass</span><span class="p">):</span>
<span class="k">raise</span> <span class="n">AccessViolation</span>
<span class="c1"># If we got here, it must be okay to access the attribute</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">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="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">return</span> <span class="n">obj</span>
<span class="c1"># tests</span>
<span class="k">class</span> <span class="nc">A</span><span class="p">(</span><span class="n">Access</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">foo</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">&#39;A&#39;</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_foo</span> <span class="o">=</span> <span class="n">foo</span>
<span class="c1"># can&#39;t set private names in __init__</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__initprivate</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__initprivate</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="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span>
<span class="k">def</span> <span class="nf">getfoo</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">_foo</span>
<span class="k">def</span> <span class="nf">setfoo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">newfoo</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_foo</span> <span class="o">=</span> <span class="n">newfoo</span>
<span class="k">def</span> <span class="nf">getname</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">_name</span>
<span class="n">A</span><span class="o">.</span><span class="n">__access__</span> <span class="o">=</span> <span class="p">{</span><span class="s1">&#39;_foo&#39;</span> <span class="p">:</span> <span class="p">(</span><span class="n">PROTECTED</span><span class="p">,</span> <span class="n">A</span><span class="p">),</span>
<span class="s1">&#39;_name&#39;</span> <span class="p">:</span> <span class="p">(</span><span class="n">PRIVATE</span><span class="p">,</span> <span class="n">A</span><span class="p">),</span>
<span class="s1">&#39;__dict__&#39;</span> <span class="p">:</span> <span class="p">(</span><span class="n">PRIVATE</span><span class="p">,</span> <span class="n">A</span><span class="p">),</span>
<span class="s1">&#39;__access__&#39;</span><span class="p">:</span> <span class="p">(</span><span class="n">PRIVATE</span><span class="p">,</span> <span class="n">A</span><span class="p">),</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">B</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">setfoo</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">newfoo</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_foo</span> <span class="o">=</span> <span class="n">newfoo</span> <span class="o">+</span> <span class="mi">3</span>
<span class="k">def</span> <span class="nf">setname</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="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">B</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">b</span><span class="o">.</span><span class="n">getfoo</span><span class="p">()</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">a</span><span class="o">.</span><span class="n">getfoo</span><span class="p">()</span> <span class="o">==</span> <span class="mi">1</span>
<span class="n">a</span><span class="o">.</span><span class="n">setfoo</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">a</span><span class="o">.</span><span class="n">getfoo</span><span class="p">()</span> <span class="o">==</span> <span class="mi">2</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">a</span><span class="o">.</span><span class="n">_foo</span>
<span class="k">except</span> <span class="n">AccessViolation</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;AccessViolation expected&#39;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">a</span><span class="o">.</span><span class="n">_foo</span> <span class="o">=</span> <span class="mi">3</span>
<span class="k">except</span> <span class="n">AccessViolation</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;AccessViolation expected&#39;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">a</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="s1">&#39;_foo&#39;</span><span class="p">]</span>
<span class="k">except</span> <span class="n">AccessViolation</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;AccessViolation expected&#39;</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">B</span><span class="p">()</span>
<span class="k">assert</span> <span class="n">b</span><span class="o">.</span><span class="n">getfoo</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span>
<span class="n">b</span><span class="o">.</span><span class="n">setfoo</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">b</span><span class="o">.</span><span class="n">getfoo</span><span class="p">()</span> <span class="o">==</span> <span class="mi">5</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">b</span><span class="o">.</span><span class="n">setname</span><span class="p">(</span><span class="s1">&#39;B&#39;</span><span class="p">)</span>
<span class="k">except</span> <span class="n">AccessViolation</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">assert</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;AccessViolation expected&#39;</span>
<span class="k">assert</span> <span class="n">b</span><span class="o">.</span><span class="n">getname</span><span class="p">()</span> <span class="o">==</span> <span class="s1">&#39;A&#39;</span>
</pre></div>
</div>
<p>Heres an implementation of the attribute hook described in PEP
213 (except that hooking on attribute deletion isnt supported by
the current reference implementation).</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Pep213</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__findattr__</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="o">*</span><span class="n">args</span><span class="p">):</span>
<span class="n">hookname</span> <span class="o">=</span> <span class="s1">&#39;__attr_</span><span class="si">%s</span><span class="s1">__&#39;</span> <span class="o">%</span> <span class="n">name</span>
<span class="k">if</span> <span class="n">args</span><span class="p">:</span>
<span class="n">op</span> <span class="o">=</span> <span class="s1">&#39;set&#39;</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">op</span> <span class="o">=</span> <span class="s1">&#39;get&#39;</span>
<span class="c1"># XXX: op = &#39;del&#39; currently not supported</span>
<span class="n">missing</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">meth</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hookname</span><span class="p">,</span> <span class="n">missing</span><span class="p">)</span>
<span class="k">if</span> <span class="n">meth</span> <span class="ow">is</span> <span class="n">missing</span><span class="p">:</span>
<span class="k">if</span> <span class="n">op</span> <span class="o">==</span> <span class="s1">&#39;set&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">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="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">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="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">meth</span><span class="p">(</span><span class="n">op</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">computation</span><span class="p">(</span><span class="n">i</span><span class="p">):</span>
<span class="nb">print</span> <span class="s1">&#39;doing computation:&#39;</span><span class="p">,</span> <span class="n">i</span>
<span class="k">return</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">3</span>
<span class="k">def</span> <span class="nf">rev_computation</span><span class="p">(</span><span class="n">i</span><span class="p">):</span>
<span class="nb">print</span> <span class="s1">&#39;doing rev_computation:&#39;</span><span class="p">,</span> <span class="n">i</span>
<span class="k">return</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">3</span>
<span class="k">class</span> <span class="nc">X</span><span class="p">(</span><span class="n">Pep213</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">foo</span><span class="o">=</span><span class="mi">0</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__foo</span> <span class="o">=</span> <span class="n">foo</span>
<span class="k">def</span> <span class="nf">__attr_foo__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">op</span><span class="p">,</span> <span class="n">val</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">op</span> <span class="o">==</span> <span class="s1">&#39;get&#39;</span><span class="p">:</span>
<span class="k">return</span> <span class="n">computation</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__foo</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">op</span> <span class="o">==</span> <span class="s1">&#39;set&#39;</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__foo</span> <span class="o">=</span> <span class="n">rev_computation</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
<span class="c1"># XXX: &#39;del&#39; not yet supported</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">X</span><span class="p">()</span>
<span class="n">fooval</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="n">foo</span>
<span class="nb">print</span> <span class="n">fooval</span>
<span class="n">x</span><span class="o">.</span><span class="n">foo</span> <span class="o">=</span> <span class="n">fooval</span> <span class="o">+</span> <span class="mi">5</span>
<span class="nb">print</span> <span class="n">x</span><span class="o">.</span><span class="n">foo</span>
<span class="c1"># del x.foo</span>
</pre></div>
</div>
</section>
<section id="reference-implementation">
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
<p>The reference implementation, as a patch to the Python core, can be
found at this URL:</p>
<p><a class="reference external" href="http://sourceforge.net/patch/?func=detailpatch&amp;patch_id=102613&amp;group_id=5470">http://sourceforge.net/patch/?func=detailpatch&amp;patch_id=102613&amp;group_id=5470</a></p>
</section>
<section id="references">
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id7" role="doc-footnote">
<dt class="label" id="id7">[<a href="#id1">1</a>]</dt>
<dd><a class="reference external" href="http://docs.python.org/reference/datamodel.html#customizing-attribute-access">http://docs.python.org/reference/datamodel.html#customizing-attribute-access</a></aside>
<aside class="footnote brackets" id="id8" role="doc-footnote">
<dt class="label" id="id8">[<a href="#id2">2</a>]</dt>
<dd><a class="reference external" href="http://www.javasoft.com/products/javabeans/">http://www.javasoft.com/products/javabeans/</a></aside>
<aside class="footnote brackets" id="id9" role="doc-footnote">
<dt class="label" id="id9">[<a href="#id3">3</a>]</dt>
<dd><a class="reference external" href="http://www.digicool.com/releases/ExtensionClass/Acquisition.html">http://www.digicool.com/releases/ExtensionClass/Acquisition.html</a></aside>
<aside class="footnote brackets" id="id10" role="doc-footnote">
<dt class="label" id="id10">[<a href="#id4">5</a>]</dt>
<dd><a class="reference external" href="http://www.digicool.com/releases/ExtensionClass">http://www.digicool.com/releases/ExtensionClass</a></aside>
<aside class="footnote brackets" id="id11" role="doc-footnote">
<dt class="label" id="id11">[<a href="#id5">6</a>]</dt>
<dd><a class="reference external" href="http://www.python.org/doc/essays/metaclasses/">http://www.python.org/doc/essays/metaclasses/</a></aside>
<aside class="footnote brackets" id="id12" role="doc-footnote">
<dt class="label" id="id12">[<a href="#id6">7</a>]</dt>
<dd><a class="reference external" href="http://www.foretec.com/python/workshops/1998-11/dd-ascher-sum.html">http://www.foretec.com/python/workshops/1998-11/dd-ascher-sum.html</a></aside>
</aside>
<ul class="simple">
<li><a class="reference external" href="http://docs.python.org/howto/regex.html">http://docs.python.org/howto/regex.html</a></li>
</ul>
</section>
<section id="rejection">
<h2><a class="toc-backref" href="#rejection" role="doc-backlink">Rejection</a></h2>
<p>There are serious problems with the recursion-protection feature.
As described here its not thread-safe, and a thread-safe solution
has other problems. In general, its not clear how helpful the
recursion-protection feature is; it makes it hard to write code
that needs to be callable inside <code class="docutils literal notranslate"><span class="pre">__findattr__</span></code> as well as outside
it. But without the recursion-protection, its hard to implement
<code class="docutils literal notranslate"><span class="pre">__findattr__</span></code> at all (since <code class="docutils literal notranslate"><span class="pre">__findattr__</span></code> would invoke itself
recursively for every attribute it tries to access). There seems
to be no good solution here.</p>
<p>Its also dubious how useful it is to support <code class="docutils literal notranslate"><span class="pre">__findattr__</span></code> both
for getting and for setting attributes <code class="docutils literal notranslate"><span class="pre">__setattr__</span></code> gets called
in all cases already.</p>
<p>The examples can all be implemented using <code class="docutils literal notranslate"><span class="pre">__getattr__</span></code> if care is
taken not to store instance variables under their own names.</p>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document has been placed in the Public Domain.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0231.rst">https://github.com/python/peps/blob/main/peps/pep-0231.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0231.rst">2023-09-09 17:39:29 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#introduction">Introduction</a></li>
<li><a class="reference internal" href="#background">Background</a></li>
<li><a class="reference internal" href="#proposal">Proposal</a></li>
<li><a class="reference internal" href="#key-differences-with-the-existing-protocol">Key Differences with the Existing Protocol</a></li>
<li><a class="reference internal" href="#related-work">Related Work</a></li>
<li><a class="reference internal" href="#examples">Examples</a></li>
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#rejection">Rejection</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0231.rst">Page Source (GitHub)</a>
</nav>
</section>
<script src="../_static/colour_scheme.js"></script>
<script src="../_static/wrap_tables.js"></script>
<script src="../_static/sticky_banner.js"></script>
</body>
</html>