[Tutor] OverflowError: cannot fit 'long' into an index-sized integer

Steven D'Aprano steve at pearwood.info
Wed Jan 8 12:41:53 CET 2014


On Tue, Jan 07, 2014 at 10:36:56PM -0500, Keith Winston wrote:
> And yeah, I'd like to see your Stopwatch code... I haven't looked at "with"
> yet, that's interesting. As usual, I don't totally get it...

Okay, let's talk about a common pattern in Python code:

try:
    do some stuff
finally:
    clean up

You have a "try" block which runs some code. Whatever happens inside the 
block, regardless of whether it succeeds or fails, when it is done, 
Python runs the "finally" block. Let's see this in action, first with a 
successful computation, then an unsuccessful one:


py> try:
...     print(1 + 1)
... finally:
...     print("=== Done ===")
...
2
=== Done ===

py> try:
...     print(1 + "1")
... finally:
...     print("=== Done ===")
...
=== Done ===
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'


This is such a common pattern, that a few versions back (Python 2.5, I 
believe) Python introduced new syntax for it: context managers and the 
"with" statement.

The new syntax is:

with obj as name:
    block


When Python runs the "with" statement, it expects that obj will have two 
special methods:

    * obj.__enter__ is run when the "with" statement begins, just 
      before the block is run;

    * obj.__exit__ is run when the block exits, regardless of
      whether it exited successfully, or due to an error.


There are more complications, of course, but in a nutshell that's it. A 
with statement is not terribly different from this:


name = obj.__enter__()
try:
    block
finally:
    obj.__exit__(*args)


(The args passed to __exit__ have to do with any exceptions raised 
inside the block.)



The Stopwatch code is quite simple: it has an __enter__ method which 
starts the timer, and an __exit__ method which stops it and reports how 
long things took.


=== cut ===


import gc
import sys

from functools import wraps


class Stopwatch:
    """Time hefty or long-running block of code using a ``with`` statement:

    >>> with Stopwatch():  #doctest: +SKIP
    ...     do_this()
    ...     do_that()
    ...
    time taken: 1.234567 seconds

    The Stopwatch class takes four optional arguments:

    timer::
        Timer function; by default, the default timer from the
        timeit module is used.

    allow_gc::
        If true (the default), the garbage collector is free to
        operate while the code block is running, otherwise, the
        garbage collector is temporarily disabled.

    verbose::
        If a true value (the default), the timer result is
        reported after the code block completes, and a warning
        displayed if the elapsed time is too small.

    cutoff::
        If None, elapsed time warnings are disabled; otherwise,
        the amount of time in seconds below which a warning is
        displayed. Defaults to 0.001.

    For non-interactive use, you can retrieve the time taken using the
    ``interval`` attribute:

    >>> with Stopwatch(verbose=False) as sw:  #doctest: +SKIP
    ...     do_this()
    ...     do_that()
    ...
    >>> print(sw.interval)  #doctest: +SKIP
    1.234567

    """
    def __init__(self, timer=None, allow_gc=True, verbose=True, cutoff=0.001):
        if timer is None:
            from timeit import default_timer as timer
        self.timer = timer
        self.allow_gc = allow_gc
        self.verbose = verbose
        self.cutoff = cutoff
        self.start = self.end = self.interval = None

    def __enter__(self):
        if not self.allow_gc:
            self._gc_state = gc.isenabled()
            gc.disable()
        self.interval = None
        self.start = self.timer()
        return self

    def __exit__(self, *args):
        self.end = self.timer()
        if not self.allow_gc and self._gc_state:
            gc.enable()
        self.interval = self.end - self.start
        self._report()

    def _report(self):
        if not self.verbose:
            return
        if self.cutoff is not None and self.interval < self.cutoff:
            print("elapsed time is very small; consider using timeit.Timer"
                  " for micro-timings of small code snippets")
        print('time taken: %f seconds' % self.interval)


=== cut ===

And that's all there is to it!



-- 
Steven


More information about the Tutor mailing list