[Python-Dev] PEP 343 and __context__()
Nick Coghlan
ncoghlan at gmail.com
Fri Jan 20 10:21:43 CET 2006
Jason Orendorff wrote:
> I just noticed that my name is in PEP 343 attached to the idea of the
> __context__() method, and I'm slightly queasy over it.
>
> The rationale was to help e.g. decimal.DecimalContext support 'with'.
> Maybe that's a bad idea.
>
> DecimalContext has a few problems. In code where it matters, every
> function you write has to worry about it. (That is, you can't just
> write __decimal_context__ = ... at the top of the file and be done
> with it, the way you can with, say, __metaclass__.)
No, you write "decimal.setcontext(...)" instead. You then only need to worry
if you have multiple threads, and you can even fix that by modifying
decimal.DefaultContext while the program is still single threaded. The latter
approach actually works even for a single-threaded program, but should only be
done in an application script, never in an imported module.
> And
> DecimalContext doesn't fit in with generators.
It does fit actually - you simply have to remember to restore the original
context around any invocations of yield.
> sys.stdout has similar problems.
>
> It feels like PEP 343 will make people want to follow this model.
> That is, we'll see more global behavior-controlling variables in the
> future. There are grizzlier fates; I just wondered if anyone had
> thought of this.
Yeah, it came up in response to PJE's suggestion of task-local variables for
generators. The basic concept we came up with is that if you're writing a
generator that uses a specific context, remember to save the original and
restore it around any calls to yield (at least, I came up with the idea and
no-one objected to the approach [1]).
In the case of decimal.Context:
def iter_sin(iterable):
orig_ctx = decimal.getcontext()
with orig_ctx as ctx:
ctx.prec += 10
for r in iterable:
y = sin(r) # Very high precision during calculation
with orig_ctx:
yield +y # Yielded results have normal precision
# We get "ctx" back here
# We get "orig_ctx" back here
Similarly for sys.stdout (only not thread-safe due to the global state):
def log_yielded_items(iterable, logfile):
orig_stdout = sys.stdout
with redirected_stdout(logfile):
for r in iterable:
print "Yielding:", r
with redirected_stdout(orig_stdout):
yield r # stdout goes back to normal here
# stdout is going to the logfile again here
# And stdout is back to normal at the end
The key properties of a "well-behaved" context are:
1. Use thread-local state for contexts in order to be multithreading safe
decimal.Context obeys this, sys.stdout doesn't
2. Provide a way to retrieve the current state for explicit restoration
decimal.getcontext() and sys.stdout allow this as shown above
This kind of thing is always going to be a pain, but PEP 343 makes it
significantly less so.
Cheers,
Nick.
[1] http://mail.python.org/pipermail/python-dev/2005-October/057493.html
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://www.boredomandlaziness.org
More information about the Python-Dev
mailing list