[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