diff options
Diffstat (limited to 'decorator/documentation.html')
-rw-r--r-- | decorator/documentation.html | 460 |
1 files changed, 215 insertions, 245 deletions
diff --git a/decorator/documentation.html b/decorator/documentation.html index 79163a7..55936b3 100644 --- a/decorator/documentation.html +++ b/decorator/documentation.html @@ -83,10 +83,10 @@ <tr class="field"><th class="docinfo-name">E-mail:</th><td class="field-body"><a class="reference external" href="mailto:michele.simionato@gmail.com">michele.simionato@gmail.com</a></td> </tr> <tr><th class="docinfo-name">Version:</th> -<td>3.2.0 (2010-05-25)</td></tr> +<td>3.2.1 (2010-11-28)</td></tr> <tr class="field"><th class="docinfo-name">Requires:</th><td class="field-body">Python 2.4+</td> </tr> -<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/decorator/3.2.0">http://pypi.python.org/pypi/decorator/3.2.0</a></td> +<tr class="field"><th class="docinfo-name">Download page:</th><td class="field-body"><a class="reference external" href="http://pypi.python.org/pypi/decorator/3.2.1">http://pypi.python.org/pypi/decorator/3.2.1</a></td> </tr> <tr class="field"><th class="docinfo-name">Installation:</th><td class="field-body"><tt class="docutils literal">easy_install decorator</tt></td> </tr> @@ -179,24 +179,22 @@ but they do not preserve the signature. A simple implementation could be the following (notice that in general it is impossible to memoize correctly something that depends on non-hashable arguments):</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">memoize_uw</span><span class="p">(</span><span class="n">func</span><span class="p">):</span> - <span class="n">func</span><span class="o">.</span><span class="n">cache</span> <span class="o">=</span> <span class="p">{}</span> - <span class="k">def</span> <span class="nf">memoize</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">kw</span><span class="p">):</span> - <span class="k">if</span> <span class="n">kw</span><span class="p">:</span> <span class="c"># frozenset is used to ensure hashability</span> - <span class="n">key</span> <span class="o">=</span> <span class="n">args</span><span class="p">,</span> <span class="n">frozenset</span><span class="p">(</span><span class="n">kw</span><span class="o">.</span><span class="n">iteritems</span><span class="p">())</span> - <span class="k">else</span><span class="p">:</span> - <span class="n">key</span> <span class="o">=</span> <span class="n">args</span> - <span class="n">cache</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="n">cache</span> - <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">cache</span><span class="p">:</span> - <span class="k">return</span> <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> - <span class="k">else</span><span class="p">:</span> - <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">result</span> <span class="o">=</span> <span class="n">func</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">kw</span><span class="p">)</span> - <span class="k">return</span> <span class="n">result</span> - <span class="k">return</span> <span class="n">functools</span><span class="o">.</span><span class="n">update_wrapper</span><span class="p">(</span><span class="n">memoize</span><span class="p">,</span> <span class="n">func</span><span class="p">)</span> -</pre></div> - -</div> +<pre class="literal-block"> +def memoize_uw(func): + func.cache = {} + def memoize(*args, **kw): + if kw: # frozenset is used to ensure hashability + key = args, frozenset(kw.iteritems()) + else: + key = args + cache = func.cache + if key in cache: + return cache[key] + else: + cache[key] = result = func(*args, **kw) + return result + return functools.update_wrapper(memoize, func) +</pre> <p>Here we used the <a class="reference external" href="http://www.python.org/doc/2.5.2/lib/module-functools.html">functools.update_wrapper</a> utility, which has been added in Python 2.5 expressly to simplify the definition of decorators (in older versions of Python you need to copy the function attributes @@ -211,7 +209,7 @@ general <tt class="docutils literal">memoize_uw</tt> returns a function with a <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="nd">@memoize_uw</span> <span class="o">...</span> <span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> -<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> <span class="c"># simulate some long computation</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c"># simulate some long computation</span> <span class="o">...</span> <span class="k">return</span> <span class="n">x</span> </pre></div> @@ -232,10 +230,10 @@ pydoc will tell you that the function accepts a generic signature <tt class="docutils literal">*args</tt>, <tt class="docutils literal">**kw</tt>, but when you try to call the function with more than an argument, you will get an error:</p> <div class="codeblock python"> -<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">1</span><span class="p">)</span> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> <span class="o">...</span> -<span class="ne">TypeError</span><span class="p">:</span> <span class="n">f1</span><span class="p">()</span> <span class="n">takes</span> <span class="n">exactly</span> <span class="mf">1</span> <span class="n">argument</span> <span class="p">(</span><span class="mf">2</span> <span class="n">given</span><span class="p">)</span> +<span class="ne">TypeError</span><span class="p">:</span> <span class="n">f1</span><span class="p">()</span> <span class="n">takes</span> <span class="n">exactly</span> <span class="mi">1</span> <span class="n">argument</span> <span class="p">(</span><span class="mi">2</span> <span class="n">given</span><span class="p">)</span> </pre></div> </div> @@ -257,29 +255,25 @@ returns the decorated function. The caller function must have signature <tt class="docutils literal">(f, *args, **kw)</tt> and it must call the original function <tt class="docutils literal">f</tt> with arguments <tt class="docutils literal">args</tt> and <tt class="docutils literal">kw</tt>, implementing the wanted capability, i.e. memoization in this case:</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">_memoize</span><span class="p">(</span><span class="n">func</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">kw</span><span class="p">):</span> - <span class="k">if</span> <span class="n">kw</span><span class="p">:</span> <span class="c"># frozenset is used to ensure hashability</span> - <span class="n">key</span> <span class="o">=</span> <span class="n">args</span><span class="p">,</span> <span class="n">frozenset</span><span class="p">(</span><span class="n">kw</span><span class="o">.</span><span class="n">iteritems</span><span class="p">())</span> - <span class="k">else</span><span class="p">:</span> - <span class="n">key</span> <span class="o">=</span> <span class="n">args</span> - <span class="n">cache</span> <span class="o">=</span> <span class="n">func</span><span class="o">.</span><span class="n">cache</span> <span class="c"># attributed added by memoize</span> - <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">cache</span><span class="p">:</span> - <span class="k">return</span> <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> - <span class="k">else</span><span class="p">:</span> - <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">result</span> <span class="o">=</span> <span class="n">func</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">kw</span><span class="p">)</span> - <span class="k">return</span> <span class="n">result</span> -</pre></div> - -</div> +<pre class="literal-block"> +def _memoize(func, *args, **kw): + if kw: # frozenset is used to ensure hashability + key = args, frozenset(kw.iteritems()) + else: + key = args + cache = func.cache # attributed added by memoize + if key in cache: + return cache[key] + else: + cache[key] = result = func(*args, **kw) + return result +</pre> <p>At this point you can define your decorator as follows:</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">memoize</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> - <span class="n">f</span><span class="o">.</span><span class="n">cache</span> <span class="o">=</span> <span class="p">{}</span> - <span class="k">return</span> <span class="n">decorator</span><span class="p">(</span><span class="n">_memoize</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> -</pre></div> - -</div> +<pre class="literal-block"> +def memoize(f): + f.cache = {} + return decorator(_memoize, f) +</pre> <p>The difference with respect to the <tt class="docutils literal">memoize_uw</tt> approach, which is based on nested functions, is that the decorator module forces you to lift the inner function at the outer level (<em>flat is better than nested</em>). @@ -289,7 +283,7 @@ decorate to the caller function.</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="nd">@memoize</span> <span class="o">...</span> <span class="k">def</span> <span class="nf">heavy_computation</span><span class="p">():</span> -<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">2</span><span class="p">)</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">...</span> <span class="k">return</span> <span class="s">"done"</span> <span class="o">>>></span> <span class="k">print</span> <span class="n">heavy_computation</span><span class="p">()</span> <span class="c"># the first time it will take 2 seconds</span> @@ -313,19 +307,15 @@ decorate to the caller function.</p> <p>As an additional example, here is how you can define a trivial <tt class="docutils literal">trace</tt> decorator, which prints a message everytime the traced function is called:</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">_trace</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> - <span class="k">print</span> <span class="s">"calling </span><span class="si">%s</span><span class="s"> with args </span><span class="si">%s</span><span class="s">, </span><span class="si">%s</span><span class="s">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">args</span><span class="p">,</span> <span class="n">kw</span><span class="p">)</span> - <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> -</pre></div> - -</div> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">trace</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> - <span class="k">return</span> <span class="n">decorator</span><span class="p">(</span><span class="n">_trace</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> -</pre></div> - -</div> +<pre class="literal-block"> +def _trace(f, *args, **kw): + print "calling %s with args %s, %s" % (f.__name__, args, kw) + return f(*args, **kw) +</pre> +<pre class="literal-block"> +def trace(f): + return decorator(_trace, f) +</pre> <p>Here is an example of usage:</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> @@ -336,8 +326,8 @@ function is called:</p> </div> <p>It is immediate to verify that <tt class="docutils literal">f1</tt> works</p> <div class="codeblock python"> -<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">0</span><span class="p">)</span> -<span class="n">calling</span> <span class="n">f1</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(</span><span class="mf">0</span><span class="p">,),</span> <span class="p">{}</span> +<div class="highlight"><pre><span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> +<span class="n">calling</span> <span class="n">f1</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(</span><span class="mi">0</span><span class="p">,),</span> <span class="p">{}</span> </pre></div> </div> @@ -351,27 +341,27 @@ function is called:</p> <p>The same decorator works with functions of any signature:</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> -<span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mf">1</span><span class="p">,</span> <span class="n">z</span><span class="o">=</span><span class="mf">2</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">kw</span><span class="p">):</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">z</span><span class="o">=</span><span class="mi">2</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">kw</span><span class="p">):</span> <span class="o">...</span> <span class="k">pass</span> -<span class="o">>>></span> <span class="n">f</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">3</span><span class="p">)</span> -<span class="n">calling</span> <span class="n">f</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(</span><span class="mf">0</span><span class="p">,</span> <span class="mf">3</span><span class="p">,</span> <span class="mf">2</span><span class="p">),</span> <span class="p">{}</span> +<span class="o">>>></span> <span class="n">f</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> +<span class="n">calling</span> <span class="n">f</span> <span class="k">with</span> <span class="n">args</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">{}</span> <span class="o">>>></span> <span class="k">print</span> <span class="n">getargspec</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> -<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s">'x'</span><span class="p">,</span> <span class="s">'y'</span><span class="p">,</span> <span class="s">'z'</span><span class="p">],</span> <span class="n">varargs</span><span class="o">=</span><span class="s">'args'</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="s">'kw'</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">))</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[</span><span class="s">'x'</span><span class="p">,</span> <span class="s">'y'</span><span class="p">,</span> <span class="s">'z'</span><span class="p">],</span> <span class="n">varargs</span><span class="o">=</span><span class="s">'args'</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="s">'kw'</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span> </pre></div> </div> <p>That includes even functions with exotic signatures like the following:</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> -<span class="o">...</span> <span class="k">def</span> <span class="nf">exotic_signature</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span><span class="o">=</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span><span class="mf">2</span><span class="p">)):</span> <span class="k">return</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span> +<span class="o">...</span> <span class="k">def</span> <span class="nf">exotic_signature</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span><span class="o">=</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">)):</span> <span class="k">return</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span> <span class="o">>>></span> <span class="k">print</span> <span class="n">getargspec</span><span class="p">(</span><span class="n">exotic_signature</span><span class="p">)</span> -<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[[</span><span class="s">'x'</span><span class="p">,</span> <span class="s">'y'</span><span class="p">]],</span> <span class="n">varargs</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">((</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">),))</span> +<span class="n">ArgSpec</span><span class="p">(</span><span class="n">args</span><span class="o">=</span><span class="p">[[</span><span class="s">'x'</span><span class="p">,</span> <span class="s">'y'</span><span class="p">]],</span> <span class="n">varargs</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">keywords</span><span class="o">=</span><span class="bp">None</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),))</span> <span class="o">>>></span> <span class="n">exotic_signature</span><span class="p">()</span> -<span class="n">calling</span> <span class="n">exotic_signature</span> <span class="k">with</span> <span class="n">args</span> <span class="p">((</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">),),</span> <span class="p">{}</span> -<span class="mf">3</span> +<span class="n">calling</span> <span class="n">exotic_signature</span> <span class="k">with</span> <span class="n">args</span> <span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),),</span> <span class="p">{}</span> +<span class="mi">3</span> </pre></div> </div> @@ -405,7 +395,7 @@ For instance, you can write directly</p> object which can be used as a decorator:</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="n">trace</span> -<span class="o"><</span><span class="n">function</span> <span class="n">trace</span> <span class="n">at</span> <span class="mf">0</span><span class="n">x</span><span class="o">...></span> +<span class="o"><</span><span class="n">function</span> <span class="n">trace</span> <span class="n">at</span> <span class="mi">0</span><span class="n">x</span><span class="o">...></span> </pre></div> </div> @@ -429,40 +419,38 @@ object which can be used as a decorator:</p> sometimes it is best to have back a "busy" message than to block everything. This behavior can be implemented with a suitable family of decorators, where the parameter is the busy message:</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">blocking</span><span class="p">(</span><span class="n">not_avail</span><span class="p">):</span> - <span class="k">def</span> <span class="nf">blocking</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> - <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"thread"</span><span class="p">):</span> <span class="c"># no thread running</span> - <span class="k">def</span> <span class="nf">set_result</span><span class="p">():</span> <span class="n">f</span><span class="o">.</span><span class="n">result</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> - <span class="n">f</span><span class="o">.</span><span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">set_result</span><span class="p">)</span> - <span class="n">f</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> - <span class="k">return</span> <span class="n">not_avail</span> - <span class="k">elif</span> <span class="n">f</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">isAlive</span><span class="p">():</span> - <span class="k">return</span> <span class="n">not_avail</span> - <span class="k">else</span><span class="p">:</span> <span class="c"># the thread is ended, return the stored result</span> - <span class="k">del</span> <span class="n">f</span><span class="o">.</span><span class="n">thread</span> - <span class="k">return</span> <span class="n">f</span><span class="o">.</span><span class="n">result</span> - <span class="k">return</span> <span class="n">decorator</span><span class="p">(</span><span class="n">blocking</span><span class="p">)</span> -</pre></div> - -</div> +<pre class="literal-block"> +def blocking(not_avail): + def blocking(f, *args, **kw): + if not hasattr(f, "thread"): # no thread running + def set_result(): f.result = f(*args, **kw) + f.thread = threading.Thread(None, set_result) + f.thread.start() + return not_avail + elif f.thread.isAlive(): + return not_avail + else: # the thread is ended, return the stored result + del f.thread + return f.result + return decorator(blocking) +</pre> <p>Functions decorated with <tt class="docutils literal">blocking</tt> will return a busy message if the resource is unavailable, and the intended result if the resource is available. For instance:</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="nd">@blocking</span><span class="p">(</span><span class="s">"Please wait ..."</span><span class="p">)</span> <span class="o">...</span> <span class="k">def</span> <span class="nf">read_data</span><span class="p">():</span> -<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span> <span class="c"># simulate a blocking resource</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c"># simulate a blocking resource</span> <span class="o">...</span> <span class="k">return</span> <span class="s">"some data"</span> <span class="o">>>></span> <span class="k">print</span> <span class="n">read_data</span><span class="p">()</span> <span class="c"># data is not available yet</span> <span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> -<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">>>></span> <span class="k">print</span> <span class="n">read_data</span><span class="p">()</span> <span class="c"># data is not available yet</span> <span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> -<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">>>></span> <span class="k">print</span> <span class="n">read_data</span><span class="p">()</span> <span class="c"># data is not available yet</span> <span class="n">Please</span> <span class="n">wait</span> <span class="o">...</span> @@ -490,66 +478,58 @@ is executed in a separate thread. Moreover, it is possible to set three callbacks <tt class="docutils literal">on_success</tt>, <tt class="docutils literal">on_failure</tt> and <tt class="docutils literal">on_closing</tt>, to specify how to manage the function call. The implementation is the following:</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">on_success</span><span class="p">(</span><span class="n">result</span><span class="p">):</span> <span class="c"># default implementation</span> - <span class="s">"Called on the result of the function"</span> - <span class="k">return</span> <span class="n">result</span> -</pre></div> - -</div> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">on_failure</span><span class="p">(</span><span class="n">exc_info</span><span class="p">):</span> <span class="c"># default implementation</span> - <span class="s">"Called if the function fails"</span> - <span class="k">pass</span> -</pre></div> - -</div> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">on_closing</span><span class="p">():</span> <span class="c"># default implementation</span> - <span class="s">"Called at the end, both in case of success and failure"</span> - <span class="k">pass</span> -</pre></div> - -</div> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">class</span> <span class="nc">Async</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> - <span class="sd">"""</span> -<span class="sd"> A decorator converting blocking functions into asynchronous</span> -<span class="sd"> functions, by using threads or processes. Examples:</span> - -<span class="sd"> async_with_threads = Async(threading.Thread)</span> -<span class="sd"> async_with_processes = Async(multiprocessing.Process)</span> -<span class="sd"> """</span> - - <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">threadfactory</span><span class="p">):</span> - <span class="bp">self</span><span class="o">.</span><span class="n">threadfactory</span> <span class="o">=</span> <span class="n">threadfactory</span> - - <span class="k">def</span> <span class="nf">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="n">on_success</span><span class="o">=</span><span class="n">on_success</span><span class="p">,</span> - <span class="n">on_failure</span><span class="o">=</span><span class="n">on_failure</span><span class="p">,</span> <span class="n">on_closing</span><span class="o">=</span><span class="n">on_closing</span><span class="p">):</span> - <span class="c"># every decorated function has its own independent thread counter</span> - <span class="n">func</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="n">itertools</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> - <span class="n">func</span><span class="o">.</span><span class="n">on_success</span> <span class="o">=</span> <span class="n">on_success</span> - <span class="n">func</span><span class="o">.</span><span class="n">on_failure</span> <span class="o">=</span> <span class="n">on_failure</span> - <span class="n">func</span><span class="o">.</span><span class="n">on_closing</span> <span class="o">=</span> <span class="n">on_closing</span> - <span class="k">return</span> <span class="n">decorator</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">call</span><span class="p">,</span> <span class="n">func</span><span class="p">)</span> - - <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span> - <span class="k">def</span> <span class="nf">func_wrapper</span><span class="p">():</span> - <span class="k">try</span><span class="p">:</span> - <span class="n">result</span> <span class="o">=</span> <span class="n">func</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">kw</span><span class="p">)</span> - <span class="k">except</span><span class="p">:</span> - <span class="n">func</span><span class="o">.</span><span class="n">on_failure</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">())</span> - <span class="k">else</span><span class="p">:</span> - <span class="k">return</span> <span class="n">func</span><span class="o">.</span><span class="n">on_success</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> - <span class="k">finally</span><span class="p">:</span> - <span class="n">func</span><span class="o">.</span><span class="n">on_closing</span><span class="p">()</span> - <span class="n">name</span> <span class="o">=</span> <span class="s">'</span><span class="si">%s</span><span class="s">-</span><span class="si">%s</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">func</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">func</span><span class="o">.</span><span class="n">counter</span><span class="o">.</span><span class="n">next</span><span class="p">())</span> - <span class="n">thread</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">threadfactory</span><span class="p">(</span><span class="bp">None</span><span class="p">,</span> <span class="n">func_wrapper</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> - <span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> - <span class="k">return</span> <span class="n">thread</span> -</pre></div> - -</div> +<pre class="literal-block"> +def on_success(result): # default implementation + "Called on the result of the function" + return result +</pre> +<pre class="literal-block"> +def on_failure(exc_info): # default implementation + "Called if the function fails" + pass +</pre> +<pre class="literal-block"> +def on_closing(): # default implementation + "Called at the end, both in case of success and failure" + pass +</pre> +<pre class="literal-block"> +class Async(object): + """ + A decorator converting blocking functions into asynchronous + functions, by using threads or processes. Examples: + + async_with_threads = Async(threading.Thread) + async_with_processes = Async(multiprocessing.Process) + """ + + def __init__(self, threadfactory): + self.threadfactory = threadfactory + + def __call__(self, func, on_success=on_success, + on_failure=on_failure, on_closing=on_closing): + # every decorated function has its own independent thread counter + func.counter = itertools.count(1) + func.on_success = on_success + func.on_failure = on_failure + func.on_closing = on_closing + return decorator(self.call, func) + + def call(self, func, *args, **kw): + def func_wrapper(): + try: + result = func(*args, **kw) + except: + func.on_failure(sys.exc_info()) + else: + return func.on_success(result) + finally: + func.on_closing() + name = '%s-%s' % (func.__name__, func.counter.next()) + thread = self.threadfactory(None, func_wrapper, name) + thread.start() + return thread +</pre> <p>The decorated function returns the current execution thread, which can be stored and checked later, for instance to verify that the thread <tt class="docutils literal">.isAlive()</tt>.</p> @@ -566,7 +546,7 @@ be locked. Here is a minimalistic example:</p> <span class="o">...</span> <span class="k">def</span> <span class="nf">write</span><span class="p">(</span><span class="n">data</span><span class="p">):</span> <span class="o">...</span> <span class="c"># append data to the datalist by locking</span> <span class="o">...</span> <span class="k">with</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">():</span> -<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span> <span class="c"># emulate some long running operation</span> +<span class="o">...</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c"># emulate some long running operation</span> <span class="o">...</span> <span class="n">datalist</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">...</span> <span class="c"># other operations not requiring a lock here</span> </pre></div> @@ -576,14 +556,14 @@ be locked. Here is a minimalistic example:</p> be no synchronization problems since <tt class="docutils literal">write</tt> is locked.</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="n">write</span><span class="p">(</span><span class="s">"data1"</span><span class="p">)</span> -<span class="o"><</span><span class="n">Thread</span><span class="p">(</span><span class="n">write</span><span class="o">-</span><span class="mf">1</span><span class="p">,</span> <span class="n">started</span><span class="o">...</span><span class="p">)</span><span class="o">></span> +<span class="o"><</span><span class="n">Thread</span><span class="p">(</span><span class="n">write</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">started</span><span class="o">...</span><span class="p">)</span><span class="o">></span> -<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="o">.</span><span class="mf">1</span><span class="p">)</span> <span class="c"># wait a bit, so we are sure data2 is written after data1</span> +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="o">.</span><span class="mi">1</span><span class="p">)</span> <span class="c"># wait a bit, so we are sure data2 is written after data1</span> <span class="o">>>></span> <span class="n">write</span><span class="p">(</span><span class="s">"data2"</span><span class="p">)</span> -<span class="o"><</span><span class="n">Thread</span><span class="p">(</span><span class="n">write</span><span class="o">-</span><span class="mf">2</span><span class="p">,</span> <span class="n">started</span><span class="o">...</span><span class="p">)</span><span class="o">></span> +<span class="o"><</span><span class="n">Thread</span><span class="p">(</span><span class="n">write</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">started</span><span class="o">...</span><span class="p">)</span><span class="o">></span> -<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">2</span><span class="p">)</span> <span class="c"># wait for the writers to complete</span> +<span class="o">>>></span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c"># wait for the writers to complete</span> <span class="o">>>></span> <span class="k">print</span> <span class="n">datalist</span> <span class="p">[</span><span class="s">'data1'</span><span class="p">,</span> <span class="s">'data2'</span><span class="p">]</span> @@ -610,8 +590,8 @@ were the function is generated by <tt class="docutils literal">exec</tt>. Here i <span class="o">...</span> <span class="k">print</span> <span class="n">args</span><span class="p">,</span> <span class="n">kw</span> <span class="o">>>></span> <span class="n">f1</span> <span class="o">=</span> <span class="n">FunctionMaker</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">'f1(a, b)'</span><span class="p">,</span> <span class="s">'f(a, b)'</span><span class="p">,</span> <span class="nb">dict</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="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mf">1</span><span class="p">,</span><span class="mf">2</span><span class="p">)</span> -<span class="p">(</span><span class="mf">1</span><span class="p">,</span> <span class="mf">2</span><span class="p">)</span> <span class="p">{}</span> +<span class="o">>>></span> <span class="n">f1</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span> +<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{}</span> </pre></div> </div> @@ -674,14 +654,12 @@ available</tt>. In the past I have considered this acceptable, since <tt class="docutils literal">inspect.getsource</tt> does not really work even with regular decorators. In that case <tt class="docutils literal">inspect.getsource</tt> gives you the wrapper source code which is probably not what you want:</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">identity_dec</span><span class="p">(</span><span class="n">func</span><span class="p">):</span> - <span class="k">def</span> <span class="nf">wrapper</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">kw</span><span class="p">):</span> - <span class="k">return</span> <span class="n">func</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">kw</span><span class="p">)</span> - <span class="k">return</span> <span class="n">wrapper</span> -</pre></div> - -</div> +<pre class="literal-block"> +def identity_dec(func): + def wrapper(*args, **kw): + return func(*args, **kw) + return wrapper +</pre> <div class="codeblock python"> <div class="highlight"><pre><span class="nd">@identity_dec</span> <span class="k">def</span> <span class="nf">example</span><span class="p">():</span> <span class="k">pass</span> @@ -694,7 +672,7 @@ source code which is probably not what you want:</p> </div> <p>(see bug report <a class="reference external" href="http://bugs.python.org/issue1764286">1764286</a> for an explanation of what is happening). -Unfortunately the bug is still there, even in Python 2.6 and 3.0. +Unfortunately the bug is still there, even in Python 2.7 and 3.1. There is however a workaround. The decorator module adds an attribute <tt class="docutils literal">.undecorated</tt> to the decorated function, containing a reference to the original function. The easy way to get @@ -703,10 +681,10 @@ undecorated function:</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span> <span class="n">inspect</span><span class="o">.</span><span class="n">getsource</span><span class="p">(</span><span class="n">factorial</span><span class="o">.</span><span class="n">undecorated</span><span class="p">)</span> <span class="nd">@tail_recursive</span> -<span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">acc</span><span class="o">=</span><span class="mf">1</span><span class="p">):</span> +<span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">acc</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span> <span class="s">"The good old factorial"</span> - <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mf">0</span><span class="p">:</span> <span class="k">return</span> <span class="n">acc</span> - <span class="k">return</span> <span class="n">factorial</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mf">1</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="n">acc</span><span class="p">)</span> + <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="n">acc</span> + <span class="k">return</span> <span class="n">factorial</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="n">acc</span><span class="p">)</span> <span class="o"><</span><span class="n">BLANKLINE</span><span class="o">></span> </pre></div> @@ -720,18 +698,16 @@ decorator is not signature-preserving. Therefore you may want an easy way to upgrade third party decorators to signature-preserving decorators without having to rewrite them in terms of <tt class="docutils literal">decorator</tt>. You can use a <tt class="docutils literal">FunctionMaker</tt> to implement that functionality as follows:</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">decorator_apply</span><span class="p">(</span><span class="n">dec</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span> - <span class="sd">"""</span> -<span class="sd"> Decorate a function by preserving the signature even if dec</span> -<span class="sd"> is not a signature-preserving decorator.</span> -<span class="sd"> """</span> - <span class="k">return</span> <span class="n">FunctionMaker</span><span class="o">.</span><span class="n">create</span><span class="p">(</span> - <span class="n">func</span><span class="p">,</span> <span class="s">'return decorated(</span><span class="si">%(signature)s</span><span class="s">)'</span><span class="p">,</span> - <span class="nb">dict</span><span class="p">(</span><span class="n">decorated</span><span class="o">=</span><span class="n">dec</span><span class="p">(</span><span class="n">func</span><span class="p">)),</span> <span class="n">undecorated</span><span class="o">=</span><span class="n">func</span><span class="p">)</span> -</pre></div> - -</div> +<pre class="literal-block"> +def decorator_apply(dec, func): + """ + Decorate a function by preserving the signature even if dec + is not a signature-preserving decorator. + """ + return FunctionMaker.create( + func, 'return decorated(%(signature)s)', + dict(decorated=dec(func)), undecorated=func) +</pre> <p><tt class="docutils literal">decorator_apply</tt> sets the attribute <tt class="docutils literal">.undecorated</tt> of the generated function to the original function, so that you can get the right source code.</p> @@ -745,60 +721,54 @@ pretty slick decorator that converts a tail-recursive function in an iterative function. I have shamelessly stolen the basic idea from Kay Schluehr's recipe in the Python Cookbook, <a class="reference external" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691">http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691</a>.</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">class</span> <span class="nc">TailRecursive</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> - <span class="sd">"""</span> -<span class="sd"> tail_recursive decorator based on Kay Schluehr's recipe</span> -<span class="sd"> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691</span> -<span class="sd"> with improvements by me and George Sakkis.</span> -<span class="sd"> """</span> - - <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span> - <span class="bp">self</span><span class="o">.</span><span class="n">func</span> <span class="o">=</span> <span class="n">func</span> - <span class="bp">self</span><span class="o">.</span><span class="n">firstcall</span> <span class="o">=</span> <span class="bp">True</span> - <span class="bp">self</span><span class="o">.</span><span class="n">CONTINUE</span> <span class="o">=</span> <span class="nb">object</span><span class="p">()</span> <span class="c"># sentinel</span> - - <span class="k">def</span> <span class="nf">__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">kwd</span><span class="p">):</span> - <span class="n">CONTINUE</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">CONTINUE</span> - <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">firstcall</span><span class="p">:</span> - <span class="n">func</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">func</span> - <span class="bp">self</span><span class="o">.</span><span class="n">firstcall</span> <span class="o">=</span> <span class="bp">False</span> - <span class="k">try</span><span class="p">:</span> - <span class="k">while</span> <span class="bp">True</span><span class="p">:</span> - <span class="n">result</span> <span class="o">=</span> <span class="n">func</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">kwd</span><span class="p">)</span> - <span class="k">if</span> <span class="n">result</span> <span class="ow">is</span> <span class="n">CONTINUE</span><span class="p">:</span> <span class="c"># update arguments</span> - <span class="n">args</span><span class="p">,</span> <span class="n">kwd</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">argskwd</span> - <span class="k">else</span><span class="p">:</span> <span class="c"># last call</span> - <span class="k">return</span> <span class="n">result</span> - <span class="k">finally</span><span class="p">:</span> - <span class="bp">self</span><span class="o">.</span><span class="n">firstcall</span> <span class="o">=</span> <span class="bp">True</span> - <span class="k">else</span><span class="p">:</span> <span class="c"># return the arguments of the tail call</span> - <span class="bp">self</span><span class="o">.</span><span class="n">argskwd</span> <span class="o">=</span> <span class="n">args</span><span class="p">,</span> <span class="n">kwd</span> - <span class="k">return</span> <span class="n">CONTINUE</span> -</pre></div> - -</div> +<pre class="literal-block"> +class TailRecursive(object): + """ + tail_recursive decorator based on Kay Schluehr's recipe + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691 + with improvements by me and George Sakkis. + """ + + def __init__(self, func): + self.func = func + self.firstcall = True + self.CONTINUE = object() # sentinel + + def __call__(self, *args, **kwd): + CONTINUE = self.CONTINUE + if self.firstcall: + func = self.func + self.firstcall = False + try: + while True: + result = func(*args, **kwd) + if result is CONTINUE: # update arguments + args, kwd = self.argskwd + else: # last call + return result + finally: + self.firstcall = True + else: # return the arguments of the tail call + self.argskwd = args, kwd + return CONTINUE +</pre> <p>Here the decorator is implemented as a class returning callable objects.</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">tail_recursive</span><span class="p">(</span><span class="n">func</span><span class="p">):</span> - <span class="k">return</span> <span class="n">decorator_apply</span><span class="p">(</span><span class="n">TailRecursive</span><span class="p">,</span> <span class="n">func</span><span class="p">)</span> -</pre></div> - -</div> +<pre class="literal-block"> +def tail_recursive(func): + return decorator_apply(TailRecursive, func) +</pre> <p>Here is how you apply the upgraded decorator to the good old factorial:</p> +<pre class="literal-block"> +@tail_recursive +def factorial(n, acc=1): + "The good old factorial" + if n == 0: return acc + return factorial(n-1, n*acc) +</pre> <div class="codeblock python"> -<div class="highlight"><pre><span class="nd">@tail_recursive</span> -<span class="k">def</span> <span class="nf">factorial</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">acc</span><span class="o">=</span><span class="mf">1</span><span class="p">):</span> - <span class="s">"The good old factorial"</span> - <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mf">0</span><span class="p">:</span> <span class="k">return</span> <span class="n">acc</span> - <span class="k">return</span> <span class="n">factorial</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mf">1</span><span class="p">,</span> <span class="n">n</span><span class="o">*</span><span class="n">acc</span><span class="p">)</span> -</pre></div> - -</div> -<div class="codeblock python"> -<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span> <span class="n">factorial</span><span class="p">(</span><span class="mf">4</span><span class="p">)</span> -<span class="mf">24</span> +<div class="highlight"><pre><span class="o">>>></span> <span class="k">print</span> <span class="n">factorial</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> +<span class="mi">24</span> </pre></div> </div> @@ -807,13 +777,11 @@ your mind ;) Notice that there is no recursion limit now, and you can easily compute <tt class="docutils literal">factorial(1001)</tt> or larger without filling the stack frame. Notice also that the decorator will not work on functions which are not tail recursive, such as the following</p> -<div class="codeblock python"> -<div class="highlight"><pre><span class="k">def</span> <span class="nf">fact</span><span class="p">(</span><span class="n">n</span><span class="p">):</span> <span class="c"># this is not tail-recursive</span> - <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mf">0</span><span class="p">:</span> <span class="k">return</span> <span class="mf">1</span> - <span class="k">return</span> <span class="n">n</span> <span class="o">*</span> <span class="n">fact</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mf">1</span><span class="p">)</span> -</pre></div> - -</div> +<pre class="literal-block"> +def fact(n): # this is not tail-recursive + if n == 0: return 1 + return n * fact(n-1) +</pre> <p>(reminder: a function is tail recursive if it either returns a value without making a recursive call, or returns directly the result of a recursive call).</p> @@ -859,7 +827,7 @@ longer and more difficult to understand. Consider this example:</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="nd">@trace</span> <span class="o">...</span> <span class="k">def</span> <span class="nf">f</span><span class="p">():</span> -<span class="o">...</span> <span class="mf">1</span><span class="o">/</span><span class="mf">0</span> +<span class="o">...</span> <span class="mi">1</span><span class="o">/</span><span class="mi">0</span> </pre></div> </div> @@ -869,11 +837,11 @@ function is decorated the traceback will be longer:</p> <div class="highlight"><pre><span class="o">>>></span> <span class="n">f</span><span class="p">()</span> <span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> <span class="o">...</span> - <span class="n">File</span> <span class="s">"<string>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span> - <span class="n">File</span> <span class="s">"<doctest __main__[18]>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">4</span><span class="p">,</span> <span class="ow">in</span> <span class="n">trace</span> + <span class="n">File</span> <span class="s">"<string>"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">2</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span> + <span class="n">File</span> <span class="s">"<doctest __main__[18]>"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">4</span><span class="p">,</span> <span class="ow">in</span> <span class="n">trace</span> <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span> - <span class="n">File</span> <span class="s">"<doctest __main__[47]>"</span><span class="p">,</span> <span class="n">line</span> <span class="mf">3</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span> - <span class="mf">1</span><span class="o">/</span><span class="mf">0</span> + <span class="n">File</span> <span class="s">"<doctest __main__[47]>"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">3</span><span class="p">,</span> <span class="ow">in</span> <span class="n">f</span> + <span class="mi">1</span><span class="o">/</span><span class="mi">0</span> <span class="ne">ZeroDivisionError</span><span class="p">:</span> <span class="n">integer</span> <span class="n">division</span> <span class="ow">or</span> <span class="n">modulo</span> <span class="n">by</span> <span class="n">zero</span> </pre></div> @@ -926,7 +894,7 @@ called too late:</p> <p>The solution is to extract the inner function from the unbound method:</p> <div class="codeblock python"> <div class="highlight"><pre><span class="o">>>></span> <span class="n">trace</span><span class="p">(</span><span class="n">C</span><span class="o">.</span><span class="n">meth</span><span class="o">.</span><span class="n">im_func</span><span class="p">)</span> -<span class="o"><</span><span class="n">function</span> <span class="n">meth</span> <span class="n">at</span> <span class="mf">0</span><span class="n">x</span><span class="o">...></span> +<span class="o"><</span><span class="n">function</span> <span class="n">meth</span> <span class="n">at</span> <span class="mi">0</span><span class="n">x</span><span class="o">...></span> </pre></div> </div> @@ -945,7 +913,9 @@ you will get a <tt class="docutils literal">NameError</tt>:</p> </pre></div> </div> -<p>Finally, the implementation is such that the decorated function contains +<p>Finally, the implementation is such that the decorated function +attribute <tt class="docutils literal">.func_globals</tt> is a <em>copy</em> of the original function +attribute. Moreover the decorated function contains a <em>copy</em> of the original function dictionary (<tt class="docutils literal">vars(decorated_f) is not vars(f)</tt>):</p> <div class="codeblock python"> |