752 lines
74 KiB
HTML
752 lines
74 KiB
HTML
|
||
<!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> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </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 <barry at python.org></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
|
||
Python’s 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 Zope’s 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 aren’t “real” Python instances,
|
||
some aspects of the Python runtime system don’t 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 Python’s 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 instance’s <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 instance’s <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
|
||
instance’s 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 it’s 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">'_'</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">'_set_'</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">'_get_'</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">'<Wrapper: [</span><span class="si">%s</span><span class="s1"> | </span><span class="si">%s</span><span class="s1">]>'</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">'_WrapperImplicit__'</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">'aq_parent'</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">'aq_self'</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">'aq_base'</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">'_'</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">'__of__'</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">'<Wrapper: [</span><span class="si">%s</span><span class="s1"> | </span><span class="si">%s</span><span class="s1">]>'</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">'_WrapperExplicit__'</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">'aq_parent'</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">'aq_self'</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">'aq_base'</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">'aq_acquire'</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">'__of__'</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">'__of__'</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">'red'</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">'red'</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">'green'</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">'green'</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">'AttributeError expected'</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">'purple'</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">'AttributeError expected'</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">'pink'</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">'color'</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">'pink'</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">'cyan'</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">'cyan'</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">'AttributeError expected'</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">></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">'call stack is not deep enough'</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">'__cache__'</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'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'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">'__access__'</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'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">'__init__'</span><span class="p">:</span>
|
||
<span class="c1"># There aren't entries in the cache for ctors,</span>
|
||
<span class="c1"># because the calling mechanism doesn'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't know who's __init__ we're in,</span>
|
||
<span class="c1"># for now we'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">'A'</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'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">'_foo'</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">'_name'</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">'__dict__'</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">'__access__'</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">'AccessViolation expected'</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">'AccessViolation expected'</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">'_foo'</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">'AccessViolation expected'</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">'B'</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">'AccessViolation expected'</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">'A'</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here’s an implementation of the attribute hook described in PEP
|
||
213 (except that hooking on attribute deletion isn’t 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">'__attr_</span><span class="si">%s</span><span class="s1">__'</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">'set'</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">op</span> <span class="o">=</span> <span class="s1">'get'</span>
|
||
<span class="c1"># XXX: op = 'del' 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">'set'</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">'doing computation:'</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">'doing rev_computation:'</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">'get'</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">'set'</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: 'del' 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&patch_id=102613&group_id=5470">http://sourceforge.net/patch/?func=detailpatch&patch_id=102613&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 it’s not thread-safe, and a thread-safe solution
|
||
has other problems. In general, it’s 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, it’s 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>It’s 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> |