* Robert Kern:
On 2010-03-04 10:56 AM, Alf P. Steinbach wrote:
* Robert Kern:
On 2010-03-03 18:49 PM, Alf P. Steinbach wrote:
[snippety]
If you call the possibly failing operation "A", then that systematic
approach goes like this: if A fails, then it has cleaned up its own
mess, but if A succeeds, then it's the responsibility of the calling
code to clean up if the higher level (multiple statements) operation
that A is embedded in, fails.
And that's what Marginean's original C++ ScopeGuard was designed for,
and what the corresponding Python Cleanup class is designed for.
And try: finally:, for that matter.
Not to mention "with".
Some other poster made the same error recently in this thread; it is a
common fallacy in discussions about programming, to assume that since
the same can be expressed using lower level constructs, those are all
that are required.
If adopted as true it ultimately means the removal of all control
structures above the level of "if" and "goto" (except Python doesn't
have "goto").
What I'm trying to explain is that the with: statement has a use even if
Cleanup doesn't. Arguing that Cleanup doesn't improve on try: finally:
does not mean that the with: statement doesn't improve on try: finally:.
That's a different argument, essentially that you see no advantage for your
current coding patterns.
It's unconnected to the argument I responded to.
The argument that I responded to, that the possibility of expressing things at
the level of try:finally: means that a higher level construct is superfluous, is
still meaningless.
Both formulations can be correct (and both work perfectly fine with
the chdir() example being used). Sometimes one is better than the
other, and sometimes not. You can achieve both ways with either your
Cleanup class or with try: finally:.
I am still of the opinion that Cleanup is not an improvement over try:
finally: and has the significant ugliness of forcing cleanup code into
callables. This significantly limits what you can do in your cleanup
code.
Uhm, not really. :-) As I see it.
Well, not being able to affect the namespace is a significant
limitation. Sometimes you need to delete objects from the namespace in
order to ensure that their refcounts go to zero and their cleanup code
gets executed.
Just a nit (I agree that a lambda can't do this, but as to what's
required): assigning None is sufficient for that[1].
Yes, but no callable is going to allow you to assign None to names in
that namespace, either. Not without sys._getframe() hackery, in any case.
However, note that the current language doesn't guarantee such cleanup,
at least as far as I know.
So while it's good practice to support it, to do everything to let it
happen, it's presumably bad practice to rely on it happening.
Tracebacks will keep the namespace alive and all objects in it.
Thanks!, I hadn't thought of connecting that to general cleanup actions.
It limits the use of general "with" in the same way.
Not really.
Sorry, it limits general 'with' in /exactly/ the same way.
It's easy to write context managers that do that [delete objects from the
namespace].
Sorry, no can do, as far as I know; your following example quoted below is an
example of /something else/.
And adding on top of irrelevancy, for the pure technical aspect it can be
accomplished in the same way using Cleanup (I provide an example below).
However, doing that would generally be worse than pointless since with good
coding practices the objects would become unreferenced anyway.
You put
the initialization code in the __enter__() method, assign whatever
objects you want to keep around through the with: clause as attributes
on the manager, then delete those attributes in the __exit__().
Analogously, if one were to do this thing, then it could be accomplished using a
Cleanup context manager as follows:
foo = lambda: None
foo.x = create_some_object()
at_cleanup.call( lambda o = foo: delattr( o, "x" ) )
... except that
1) for a once-only case this is less code :-)
2) it is a usage that I wouldn't recommend; instead I recommend adopting good
coding practices where object references aren't kept around.
Or, you
use the @contextmanager decorator to turn a generator into a context
manager, and you just assign to local variables and del them in the
finally: clause.
Uhm, you don't need a 'finally' clause when you define a context manager.
Additionally, you don't need to 'del' the local variables in @contextmanager
decorated generator.
The local variables cease to exist automatically.
What you can't do is write a generic context manager where the
initialization happens inside the with: clause and the cleanup actions
are registered callables. That does not allow you to affect the namespace.
If you mean that you can't introduce direct local variables and have them
deleted by "registered callables" in a portable way, then right.
But I can't think of any example where that would be relevant; in particular
what matters for supporting on-destruction cleanup is whether you keep any
references or not, not whether you have a local variable of any given name.
And I think "with" is quite useful even with that restriction.
Cheers,
- Alf
--
http://mail.python.org/mailman/listinfo/python-list