619 lines
62 KiB
HTML
619 lines
62 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 280 – Optimizing access to globals | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0280/">
|
||
<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 280 – Optimizing access to globals | peps.python.org'>
|
||
<meta property="og:description" content="This PEP describes yet another approach to optimizing access to module globals, providing an alternative to PEP 266 (Optimizing Global Variable/Attribute Access by Skip Montanaro) and PEP 267 (Optimized Access to Module Namespaces by Jeremy Hylton).">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0280/">
|
||
<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 yet another approach to optimizing access to module globals, providing an alternative to PEP 266 (Optimizing Global Variable/Attribute Access by Skip Montanaro) and PEP 267 (Optimized Access to Module Namespaces by Jeremy Hylton).">
|
||
<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 280</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 280 – Optimizing access to globals</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Guido van Rossum <guido at python.org></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Inactive draft that may be taken up again at a later time">Deferred</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">10-Feb-2002</dd>
|
||
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-odd">2.3</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="#deferral">Deferral</a></li>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#description">Description</a></li>
|
||
<li><a class="reference internal" href="#additional-ideas">Additional Ideas</a></li>
|
||
<li><a class="reference internal" href="#faqs">FAQs</a></li>
|
||
<li><a class="reference internal" href="#graphics">Graphics</a></li>
|
||
<li><a class="reference internal" href="#comparison">Comparison</a></li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="deferral">
|
||
<h2><a class="toc-backref" href="#deferral" role="doc-backlink">Deferral</a></h2>
|
||
<p>While this PEP is a nice idea, no-one has yet emerged to do the work of
|
||
hashing out the differences between this PEP, <a class="pep reference internal" href="../pep-0266/" title="PEP 266 – Optimizing Global Variable/Attribute Access">PEP 266</a> and <a class="pep reference internal" href="../pep-0267/" title="PEP 267 – Optimized Access to Module Namespaces">PEP 267</a>.
|
||
Hence, it is being deferred.</p>
|
||
</section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP describes yet another approach to optimizing access to
|
||
module globals, providing an alternative to <a class="pep reference internal" href="../pep-0266/" title="PEP 266 – Optimizing Global Variable/Attribute Access">PEP 266</a> (Optimizing
|
||
Global Variable/Attribute Access by Skip Montanaro) and <a class="pep reference internal" href="../pep-0267/" title="PEP 267 – Optimized Access to Module Namespaces">PEP 267</a>
|
||
(Optimized Access to Module Namespaces by Jeremy Hylton).</p>
|
||
<p>The expectation is that eventually one approach will be picked and
|
||
implemented; possibly multiple approaches will be prototyped
|
||
first.</p>
|
||
</section>
|
||
<section id="description">
|
||
<h2><a class="toc-backref" href="#description" role="doc-backlink">Description</a></h2>
|
||
<p>(Note: Jason Orendorff writes: “””I implemented this once, long
|
||
ago, for Python 1.5-ish, I believe. I got it to the point where
|
||
it was only 15% slower than ordinary Python, then abandoned it.
|
||
;) In my implementation, “cells” were real first-class objects,
|
||
and “celldict” was a copy-and-hack version of dictionary. I
|
||
forget how the rest worked.””” Reference:
|
||
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2002-February/019876.html">https://mail.python.org/pipermail/python-dev/2002-February/019876.html</a>)</p>
|
||
<p>Let a cell be a really simple Python object, containing a pointer
|
||
to a Python object and a pointer to a cell. Both pointers may be
|
||
<code class="docutils literal notranslate"><span class="pre">NULL</span></code>. A Python implementation could be:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">cell</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">NULL</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">cellptr</span> <span class="o">=</span> <span class="n">NULL</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The cellptr attribute is used for chaining cells together for
|
||
searching built-ins; this will be explained later.</p>
|
||
<p>Let a celldict be a mapping from strings (the names of a module’s
|
||
globals) to objects (the values of those globals), implemented
|
||
using a dict of cells. A Python implementation could be:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">celldict</span><span class="p">(</span><span class="nb">object</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="bp">self</span><span class="o">.</span><span class="n">__dict</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># dict of cells</span>
|
||
|
||
<span class="k">def</span> <span class="nf">getcell</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">c</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">cell</span><span class="p">()</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">c</span>
|
||
<span class="k">return</span> <span class="n">c</span>
|
||
|
||
<span class="k">def</span> <span class="nf">cellkeys</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">__dict</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">c</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">KeyError</span><span class="p">,</span> <span class="n">key</span>
|
||
<span class="n">value</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span>
|
||
<span class="k">if</span> <span class="n">value</span> <span class="ow">is</span> <span class="n">NULL</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">KeyError</span><span class="p">,</span> <span class="n">key</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">value</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__setitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">c</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">cell</span><span class="p">()</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">c</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">value</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__delitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">c</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="n">NULL</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">KeyError</span><span class="p">,</span> <span class="n">key</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">NULL</span>
|
||
|
||
<span class="k">def</span> <span class="nf">keys</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">[</span><span class="n">k</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">iteritems</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">NULL</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">items</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">[</span><span class="n">k</span><span class="p">,</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">iteritems</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">NULL</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">values</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">preturn</span> <span class="p">[</span><span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">itervalues</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">NULL</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">clear</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">NULL</span>
|
||
|
||
<span class="c1"># Etc.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>It is possible that a cell exists corresponding to a given key,
|
||
but the cell’s objptr is <code class="docutils literal notranslate"><span class="pre">NULL</span></code>; let’s call such a cell empty. When
|
||
the celldict is used as a mapping, it is as if empty cells don’t
|
||
exist. However, once added, a cell is never deleted from a
|
||
celldict, and it is possible to get at empty cells using the
|
||
<code class="docutils literal notranslate"><span class="pre">getcell()</span></code> method.</p>
|
||
<p>The celldict implementation never uses the cellptr attribute of
|
||
cells.</p>
|
||
<p>We change the module implementation to use a celldict for its
|
||
<code class="docutils literal notranslate"><span class="pre">__dict__</span></code>. The module’s getattr, setattr and delattr operations
|
||
now map to getitem, setitem and delitem on the celldict. The type
|
||
of <code class="docutils literal notranslate"><span class="pre"><module>.__dict__</span></code> and <code class="docutils literal notranslate"><span class="pre">globals()</span></code> is probably the only backwards
|
||
incompatibility.</p>
|
||
<p>When a module is initialized, its <code class="docutils literal notranslate"><span class="pre">__builtins__</span></code> is initialized from
|
||
the <code class="docutils literal notranslate"><span class="pre">__builtin__</span></code> module’s <code class="docutils literal notranslate"><span class="pre">__dict__</span></code>, which is itself a celldict.
|
||
For each cell in <code class="docutils literal notranslate"><span class="pre">__builtins__</span></code>, the new module’s <code class="docutils literal notranslate"><span class="pre">__dict__</span></code> adds a
|
||
cell with a <code class="docutils literal notranslate"><span class="pre">NULL</span></code> objptr, whose cellptr points to the corresponding
|
||
cell of <code class="docutils literal notranslate"><span class="pre">__builtins__</span></code>. Python pseudo-code (ignoring rexec):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">__builtin__</span>
|
||
|
||
<span class="k">class</span> <span class="nc">module</span><span class="p">(</span><span class="nb">object</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="bp">self</span><span class="o">.</span><span class="vm">__dict__</span> <span class="o">=</span> <span class="n">d</span> <span class="o">=</span> <span class="n">celldict</span><span class="p">()</span>
|
||
<span class="n">d</span><span class="p">[</span><span class="s1">'__builtins__'</span><span class="p">]</span> <span class="o">=</span> <span class="n">bd</span> <span class="o">=</span> <span class="n">__builtin__</span><span class="o">.</span><span class="vm">__dict__</span>
|
||
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">bd</span><span class="o">.</span><span class="n">cellkeys</span><span class="p">():</span>
|
||
<span class="n">c</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">getcell</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">cellptr</span> <span class="o">=</span> <span class="n">bd</span><span class="o">.</span><span class="n">getcell</span><span class="p">(</span><span class="n">k</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__getattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
|
||
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">IndexError</span><span class="p">,</span> <span class="n">k</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__setattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__delattr__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span>
|
||
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The compiler generates <code class="docutils literal notranslate"><span class="pre">LOAD_GLOBAL_CELL</span> <span class="pre"><i></span></code> (and <code class="docutils literal notranslate"><span class="pre">STORE_GLOBAL_CELL</span>
|
||
<span class="pre"><i></span></code> etc.) opcodes for references to globals, where <code class="docutils literal notranslate"><span class="pre"><i></span></code> is a small
|
||
index with meaning only within one code object like the const
|
||
index in <code class="docutils literal notranslate"><span class="pre">LOAD_CONST</span></code>. The code object has a new tuple, <code class="docutils literal notranslate"><span class="pre">co_globals</span></code>,
|
||
giving the names of the globals referenced by the code indexed by
|
||
<code class="docutils literal notranslate"><span class="pre"><i></span></code>. No new analysis is required to be able to do this.</p>
|
||
<p>When a function object is created from a code object and a celldict,
|
||
the function object creates an array of cell pointers by asking the
|
||
celldict for cells corresponding to the names in the code object’s
|
||
<code class="docutils literal notranslate"><span class="pre">co_globals</span></code>. If the celldict doesn’t already have a cell for a
|
||
particular name, it creates and an empty one. This array of cell
|
||
pointers is stored on the function object as <code class="docutils literal notranslate"><span class="pre">func_cells</span></code>. When a
|
||
function object is created from a regular dict instead of a
|
||
celldict, <code class="docutils literal notranslate"><span class="pre">func_cells</span></code> is a <code class="docutils literal notranslate"><span class="pre">NULL</span></code> pointer.</p>
|
||
<p>When the VM executes a <code class="docutils literal notranslate"><span class="pre">LOAD_GLOBAL_CELL</span> <span class="pre"><i></span></code> instruction, it gets
|
||
cell number <code class="docutils literal notranslate"><span class="pre"><i></span></code> from <code class="docutils literal notranslate"><span class="pre">func_cells</span></code>. It then looks in the cell’s
|
||
<code class="docutils literal notranslate"><span class="pre">PyObject</span></code> pointer, and if not <code class="docutils literal notranslate"><span class="pre">NULL</span></code>, that’s the global value. If it
|
||
is <code class="docutils literal notranslate"><span class="pre">NULL</span></code>, it follows the cell’s cell pointer to the next cell, if it
|
||
is not <code class="docutils literal notranslate"><span class="pre">NULL</span></code>, and looks in the <code class="docutils literal notranslate"><span class="pre">PyObject</span></code> pointer in that cell. If
|
||
that’s also <code class="docutils literal notranslate"><span class="pre">NULL</span></code>, or if there is no second cell, <code class="docutils literal notranslate"><span class="pre">NameError</span></code> is
|
||
raised. (It could follow the chain of cell pointers until a <code class="docutils literal notranslate"><span class="pre">NULL</span></code>
|
||
cell pointer is found; but I have no use for this.) Similar for
|
||
<code class="docutils literal notranslate"><span class="pre">STORE_GLOBAL_CELL</span> <span class="pre"><i></span></code>, except it doesn’t follow the cell pointer
|
||
chain – it always stores in the first cell.</p>
|
||
<p>There are fallbacks in the VM for the case where the function’s
|
||
globals aren’t a celldict, and hence <code class="docutils literal notranslate"><span class="pre">func_cells</span></code> is <code class="docutils literal notranslate"><span class="pre">NULL</span></code>. In that
|
||
case, the code object’s <code class="docutils literal notranslate"><span class="pre">co_globals</span></code> is indexed with <code class="docutils literal notranslate"><span class="pre"><i></span></code> to find the
|
||
name of the corresponding global and this name is used to index the
|
||
function’s globals dict.</p>
|
||
</section>
|
||
<section id="additional-ideas">
|
||
<h2><a class="toc-backref" href="#additional-ideas" role="doc-backlink">Additional Ideas</a></h2>
|
||
<ul>
|
||
<li>Never make <code class="docutils literal notranslate"><span class="pre">func_cell</span></code> a <code class="docutils literal notranslate"><span class="pre">NULL</span></code> pointer; instead, make up an array
|
||
of empty cells, so that <code class="docutils literal notranslate"><span class="pre">LOAD_GLOBAL_CELL</span></code> can index <code class="docutils literal notranslate"><span class="pre">func_cells</span></code>
|
||
without a <code class="docutils literal notranslate"><span class="pre">NULL</span></code> check.</li>
|
||
<li>Make <code class="docutils literal notranslate"><span class="pre">c.cellptr</span></code> equal to c when a cell is created, so that
|
||
<code class="docutils literal notranslate"><span class="pre">LOAD_GLOBAL_CELL</span></code> can always dereference <code class="docutils literal notranslate"><span class="pre">c.cellptr</span></code> without a <code class="docutils literal notranslate"><span class="pre">NULL</span></code>
|
||
check.<p>With these two additional ideas added, here’s Python pseudo-code
|
||
for <code class="docutils literal notranslate"><span class="pre">LOAD_GLOBAL_CELL</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">LOAD_GLOBAL_CELL</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">i</span><span class="p">):</span>
|
||
<span class="c1"># self is the frame</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">func_cells</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
|
||
<span class="n">obj</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span>
|
||
<span class="k">if</span> <span class="n">obj</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">NULL</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">obj</span> <span class="c1"># Existing global</span>
|
||
<span class="k">return</span> <span class="n">c</span><span class="o">.</span><span class="n">cellptr</span><span class="o">.</span><span class="n">objptr</span> <span class="c1"># Built-in or NULL</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>Be more aggressive: put the actual values of builtins into module
|
||
dicts, not just pointers to cells containing the actual values.<p>There are two points to this: (1) Simplify and speed access, which
|
||
is the most common operation. (2) Support faithful emulation of
|
||
extreme existing corner cases.</p>
|
||
<p>WRT #2, the set of builtins in the scheme above is captured at the
|
||
time a module dict is first created. Mutations to the set of builtin
|
||
names following that don’t get reflected in the module dicts. Example:
|
||
consider files <code class="docutils literal notranslate"><span class="pre">main.py</span></code> and <code class="docutils literal notranslate"><span class="pre">cheater.py</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">main</span><span class="o">.</span><span class="n">py</span><span class="p">]</span>
|
||
<span class="kn">import</span> <span class="nn">cheater</span>
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">():</span>
|
||
<span class="n">cheater</span><span class="o">.</span><span class="n">cheat</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">pachinko</span><span class="p">()</span>
|
||
<span class="nb">print</span> <span class="n">f</span><span class="p">()</span>
|
||
|
||
<span class="p">[</span><span class="n">cheater</span><span class="o">.</span><span class="n">py</span><span class="p">]</span>
|
||
<span class="k">def</span> <span class="nf">cheat</span><span class="p">():</span>
|
||
<span class="kn">import</span> <span class="nn">__builtin__</span>
|
||
<span class="n">__builtin__</span><span class="o">.</span><span class="n">pachinko</span> <span class="o">=</span> <span class="k">lambda</span><span class="p">:</span> <span class="mi">666</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If <code class="docutils literal notranslate"><span class="pre">main.py</span></code> is run under Python 2.2 (or before), 666 is printed. But
|
||
under the proposal, <code class="docutils literal notranslate"><span class="pre">__builtin__.pachinko</span></code> doesn’t exist at the time
|
||
main’s <code class="docutils literal notranslate"><span class="pre">__dict__</span></code> is initialized. When the function object for
|
||
f is created, <code class="docutils literal notranslate"><span class="pre">main.__dict__</span></code> grows a pachinko cell mapping to two
|
||
<code class="docutils literal notranslate"><span class="pre">NULLs</span></code>. When <code class="docutils literal notranslate"><span class="pre">cheat()</span></code> is called, <code class="docutils literal notranslate"><span class="pre">__builtin__.__dict__</span></code> grows a pachinko
|
||
cell too, but <code class="docutils literal notranslate"><span class="pre">main.__dict__</span></code> doesn’t know– and will never know –about
|
||
that. When f’s return stmt references pachinko, in will still find
|
||
the double-NULLs in <code class="docutils literal notranslate"><span class="pre">main.__dict__</span></code>’s <code class="docutils literal notranslate"><span class="pre">pachinko</span></code> cell, and so raise
|
||
<code class="docutils literal notranslate"><span class="pre">NameError</span></code>.</p>
|
||
<p>A similar (in cause) break in compatibility can occur if a module
|
||
global foo is del’ed, but a builtin foo was created prior to that
|
||
but after the module dict was first created. Then the builtin foo
|
||
becomes visible in the module under 2.2 and before, but remains
|
||
invisible under the proposal.</p>
|
||
<p>Mutating builtins is extremely rare (most programs never mutate the
|
||
builtins, and it’s hard to imagine a plausible use for frequent
|
||
mutation of the builtins – I’ve never seen or heard of one), so it
|
||
doesn’t matter how expensive mutating the builtins becomes. OTOH,
|
||
referencing globals and builtins is very common. Combining those
|
||
observations suggests a more aggressive caching of builtins in module
|
||
globals, speeding access at the expense of making mutations of the
|
||
builtins (potentially much) more expensive to keep the caches in
|
||
synch.</p>
|
||
<p>Much of the scheme above remains the same, and most of the rest is
|
||
just a little different. A cell changes to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">cell</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="o">=</span><span class="n">NULL</span><span class="p">,</span> <span class="n">builtin</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">objptr</span> <span class="o">=</span> <span class="n">obj</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">builtinflag</span> <span class="o">=</span> <span class="n">builtin</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>and a celldict maps strings to this version of cells. <code class="docutils literal notranslate"><span class="pre">builtinflag</span></code>
|
||
is true when and only when objptr contains a value obtained from
|
||
the builtins; in other words, it’s true when and only when a cell
|
||
is acting as a cached value. When <code class="docutils literal notranslate"><span class="pre">builtinflag</span></code> is false, objptr is
|
||
the value of a module global (possibly <code class="docutils literal notranslate"><span class="pre">NULL</span></code>). celldict changes to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">celldict</span><span class="p">(</span><span class="nb">object</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">builtindict</span><span class="o">=</span><span class="p">()):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">basedict</span> <span class="o">=</span> <span class="n">builtindict</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">__dict</span> <span class="o">=</span> <span class="n">d</span> <span class="o">=</span> <span class="p">{}</span>
|
||
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">builtindict</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
|
||
<span class="n">d</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">cell</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__getitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">c</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="n">NULL</span> <span class="ow">or</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">KeyError</span><span class="p">,</span> <span class="n">key</span>
|
||
<span class="k">return</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__setitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">c</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="n">cell</span><span class="p">()</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">c</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">value</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span> <span class="o">=</span> <span class="mi">0</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__delitem__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">c</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="n">NULL</span> <span class="ow">or</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span><span class="p">:</span>
|
||
<span class="k">raise</span> <span class="ne">KeyError</span><span class="p">,</span> <span class="n">key</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">NULL</span>
|
||
<span class="c1"># We may have unmasked a builtin. Note that because</span>
|
||
<span class="c1"># we're checking the builtin dict for that *now*, this</span>
|
||
<span class="c1"># still works if the builtin first came into existence</span>
|
||
<span class="c1"># after we were constructed. Note too that del on</span>
|
||
<span class="c1"># namespace dicts is rare, so the expense of this check</span>
|
||
<span class="c1"># shouldn't matter.</span>
|
||
<span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">basedict</span><span class="p">:</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">basedict</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
|
||
<span class="k">assert</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">NULL</span> <span class="c1"># else "in" lied</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span> <span class="o">=</span> <span class="mi">1</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># There is no builtin with the same name.</span>
|
||
<span class="k">assert</span> <span class="ow">not</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span>
|
||
|
||
<span class="k">def</span> <span class="nf">keys</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">[</span><span class="n">k</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">iteritems</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">NULL</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">items</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="p">[</span><span class="n">k</span><span class="p">,</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">iteritems</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">NULL</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">values</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="n">preturn</span> <span class="p">[</span><span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">itervalues</span><span class="p">()</span>
|
||
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">NULL</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">clear</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span><span class="p">:</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">NULL</span>
|
||
|
||
<span class="c1"># Etc.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The speed benefit comes from simplifying <code class="docutils literal notranslate"><span class="pre">LOAD_GLOBAL_CELL</span></code>, which
|
||
I expect is executed more frequently than all other namespace
|
||
operations combined:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">LOAD_GLOBAL_CELL</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">i</span><span class="p">):</span>
|
||
<span class="c1"># self is the frame</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">func_cells</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
|
||
<span class="k">return</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="c1"># may be NULL (also true before)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>That is, accessing builtins and accessing module globals are equally
|
||
fast. For module globals, a NULL-pointer test+branch is saved. For
|
||
builtins, an additional pointer chase is also saved.</p>
|
||
<p>The other part needed to make this fly is expensive, propagating
|
||
mutations of builtins into the module dicts that were initialized
|
||
from the builtins. This is much like, in 2.2, propagating changes
|
||
in new-style base classes to their descendants: the builtins need to
|
||
maintain a list of weakrefs to the modules (or module dicts)
|
||
initialized from the builtin’s dict. Given a mutation to the builtin
|
||
dict (adding a new key, changing the value associated with an
|
||
existing key, or deleting a key), traverse the list of module dicts
|
||
and make corresponding mutations to them. This is straightforward;
|
||
for example, if a key is deleted from builtins, execute
|
||
<code class="docutils literal notranslate"><span class="pre">reflect_bltin_del</span></code> in each module:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">reflect_bltin_del</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">assert</span> <span class="n">c</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="c1"># else we were already out of synch</span>
|
||
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span><span class="p">:</span>
|
||
<span class="c1"># Put us back in synch.</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">NULL</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span> <span class="o">=</span> <span class="mi">0</span>
|
||
<span class="c1"># Else we're shadowing the builtin, so don't care that</span>
|
||
<span class="c1"># the builtin went away.</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Note that <code class="docutils literal notranslate"><span class="pre">c.builtinflag</span></code> protects from us erroneously deleting a
|
||
module global of the same name. Adding a new (key, value) builtin
|
||
pair is similar:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">reflect_bltin_new</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">c</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
|
||
<span class="c1"># Never heard of it before: cache the builtin value.</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">cell</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
|
||
<span class="k">elif</span> <span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="ow">is</span> <span class="n">NULL</span><span class="p">:</span>
|
||
<span class="c1"># This used to exist in the module or the builtins,</span>
|
||
<span class="c1"># but doesn't anymore; rehabilitate it.</span>
|
||
<span class="k">assert</span> <span class="ow">not</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">value</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span> <span class="o">=</span> <span class="mi">1</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="c1"># We're shadowing it already.</span>
|
||
<span class="k">assert</span> <span class="ow">not</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Changing the value of an existing builtin:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">reflect_bltin_change</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">newvalue</span><span class="p">):</span>
|
||
<span class="n">c</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__dict</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
|
||
<span class="k">assert</span> <span class="n">c</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="c1"># else we were already out of synch</span>
|
||
<span class="k">if</span> <span class="n">c</span><span class="o">.</span><span class="n">builtinflag</span><span class="p">:</span>
|
||
<span class="c1"># Put us back in synch.</span>
|
||
<span class="n">c</span><span class="o">.</span><span class="n">objptr</span> <span class="o">=</span> <span class="n">newvalue</span>
|
||
<span class="c1"># Else we're shadowing the builtin, so don't care that</span>
|
||
<span class="c1"># the builtin changed.</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
<section id="faqs">
|
||
<h2><a class="toc-backref" href="#faqs" role="doc-backlink">FAQs</a></h2>
|
||
<ul>
|
||
<li>Q: Will it still be possible to:<p>a) install new builtins in the <code class="docutils literal notranslate"><span class="pre">__builtin__</span></code> namespace and have
|
||
them available in all already loaded modules right away ?</p>
|
||
<p>b) override builtins (e.g. <code class="docutils literal notranslate"><span class="pre">open()</span></code>) with my own copies
|
||
(e.g. to increase security) in a way that makes these new
|
||
copies override the previous ones in all modules ?</p>
|
||
<p>A: Yes, this is the whole point of this design. In the original
|
||
approach, when <code class="docutils literal notranslate"><span class="pre">LOAD_GLOBAL_CELL</span></code> finds a <code class="docutils literal notranslate"><span class="pre">NULL</span></code> in the second
|
||
cell, it should go back to see if the <code class="docutils literal notranslate"><span class="pre">__builtins__</span></code> dict has
|
||
been modified (the pseudo code doesn’t have this yet). Tim’s
|
||
“more aggressive” alternative also takes care of this.</p>
|
||
</li>
|
||
<li>Q: How does the new scheme get along with the restricted execution
|
||
model?<p>A: It is intended to support that fully.</p>
|
||
</li>
|
||
<li>Q: What happens when a global is deleted?<p>A: The module’s celldict would have a cell with a <code class="docutils literal notranslate"><span class="pre">NULL</span></code> objptr for
|
||
that key. This is true in both variations, but the “aggressive”
|
||
variation goes on to see whether this unmasks a builtin of the
|
||
same name, and if so copies its value (just a pointer-copy of the
|
||
ultimate <code class="docutils literal notranslate"><span class="pre">PyObject*</span></code>) into the cell’s objptr and sets the cell’s
|
||
<code class="docutils literal notranslate"><span class="pre">builtinflag</span></code> to true.</p>
|
||
</li>
|
||
<li>Q: What would the C code for <code class="docutils literal notranslate"><span class="pre">LOAD_GLOBAL_CELL</span></code> look like?<p>A: The first version, with the first two bullets under “Additional
|
||
ideas” incorporated, could look like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">LOAD_GLOBAL_CELL</span><span class="p">:</span>
|
||
<span class="n">cell</span> <span class="o">=</span> <span class="n">func_cells</span><span class="p">[</span><span class="n">oparg</span><span class="p">];</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">cell</span><span class="o">-></span><span class="n">objptr</span><span class="p">;</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="n">NULL</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">cell</span><span class="o">-></span><span class="n">cellptr</span><span class="o">-></span><span class="n">objptr</span><span class="p">;</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="n">NULL</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="o">...</span> <span class="n">error</span> <span class="n">recovery</span> <span class="o">...</span>
|
||
<span class="k">break</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="n">Py_INCREF</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
|
||
<span class="n">PUSH</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
|
||
<span class="k">continue</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We could even write it like this (idea courtesy of Ka-Ping Yee):</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">LOAD_GLOBAL_CELL</span><span class="p">:</span>
|
||
<span class="n">cell</span> <span class="o">=</span> <span class="n">func_cells</span><span class="p">[</span><span class="n">oparg</span><span class="p">];</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">cell</span><span class="o">-></span><span class="n">cellptr</span><span class="o">-></span><span class="n">objptr</span><span class="p">;</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">!=</span> <span class="n">NULL</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="n">Py_INCREF</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
|
||
<span class="n">PUSH</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
|
||
<span class="k">continue</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
<span class="o">...</span> <span class="n">error</span> <span class="n">recovery</span> <span class="o">...</span>
|
||
<span class="k">break</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In modern CPU architectures, this reduces the number of
|
||
branches taken for built-ins, which might be a really good
|
||
thing, while any decent memory cache should realize that
|
||
<code class="docutils literal notranslate"><span class="pre">cell->cellptr</span></code> is the same as cell for regular globals and hence
|
||
this should be very fast in that case too.</p>
|
||
<p>For the aggressive variant:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">case</span> <span class="n">LOAD_GLOBAL_CELL</span><span class="p">:</span>
|
||
<span class="n">cell</span> <span class="o">=</span> <span class="n">func_cells</span><span class="p">[</span><span class="n">oparg</span><span class="p">];</span>
|
||
<span class="n">x</span> <span class="o">=</span> <span class="n">cell</span><span class="o">-></span><span class="n">objptr</span><span class="p">;</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">!=</span> <span class="n">NULL</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="n">Py_INCREF</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
|
||
<span class="n">PUSH</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
|
||
<span class="k">continue</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
<span class="o">...</span> <span class="n">error</span> <span class="n">recovery</span> <span class="o">...</span>
|
||
<span class="k">break</span><span class="p">;</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
<li>Q: What happens in the module’s top-level code where there is
|
||
presumably no <code class="docutils literal notranslate"><span class="pre">func_cells</span></code> array?<p>A: We could do some code analysis and create a <code class="docutils literal notranslate"><span class="pre">func_cells</span></code> array,
|
||
or we could use <code class="docutils literal notranslate"><span class="pre">LOAD_NAME</span></code> which should use <code class="docutils literal notranslate"><span class="pre">PyMapping_GetItem</span></code> on
|
||
the globals dict.</p>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
<section id="graphics">
|
||
<h2><a class="toc-backref" href="#graphics" role="doc-backlink">Graphics</a></h2>
|
||
<p>Ka-Ping Yee supplied a drawing of the state of things after
|
||
“import spam”, where <code class="docutils literal notranslate"><span class="pre">spam.py</span></code> contains:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">eggs</span>
|
||
|
||
<span class="n">i</span> <span class="o">=</span> <span class="o">-</span><span class="mi">2</span>
|
||
<span class="nb">max</span> <span class="o">=</span> <span class="mi">3</span>
|
||
|
||
<span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
|
||
<span class="n">y</span> <span class="o">=</span> <span class="nb">abs</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">+</span> <span class="nb">max</span>
|
||
<span class="k">return</span> <span class="n">eggs</span><span class="o">.</span><span class="n">ham</span><span class="p">(</span><span class="n">y</span> <span class="o">+</span> <span class="n">n</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The drawing is at <a class="reference external" href="http://web.lfw.org/repo/cells.gif">http://web.lfw.org/repo/cells.gif</a>; a larger
|
||
version is at <a class="reference external" href="http://lfw.org/repo/cells-big.gif">http://lfw.org/repo/cells-big.gif</a>; the source is at
|
||
<a class="reference external" href="http://lfw.org/repo/cells.ai">http://lfw.org/repo/cells.ai</a>.</p>
|
||
</section>
|
||
<section id="comparison">
|
||
<h2><a class="toc-backref" href="#comparison" role="doc-backlink">Comparison</a></h2>
|
||
<p>XXX Here, a comparison of the three approaches could be added.</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-0280.rst">https://github.com/python/peps/blob/main/peps/pep-0280.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0280.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="#deferral">Deferral</a></li>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#description">Description</a></li>
|
||
<li><a class="reference internal" href="#additional-ideas">Additional Ideas</a></li>
|
||
<li><a class="reference internal" href="#faqs">FAQs</a></li>
|
||
<li><a class="reference internal" href="#graphics">Graphics</a></li>
|
||
<li><a class="reference internal" href="#comparison">Comparison</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-0280.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> |