From: Michael Rudolf on 4 Mar 2010 12:55 Am 04.03.2010 17:32, schrieb Jean-Michel Pichavant: > It looks like to me that 'with' statements are like decorators: overrated. Oh no, you just insulted my favourite two python features, followed immediately by generators, iterators and list comprehensions / generator expressions :p No, really: they *are* great ;D Regards, Michael
From: Jean-Michel Pichavant on 4 Mar 2010 13:22 Michael Rudolf wrote: > Am 04.03.2010 17:32, schrieb Jean-Michel Pichavant: >> It looks like to me that 'with' statements are like decorators: >> overrated. > > Oh no, you just insulted my favourite two python features, followed > immediately by generators, iterators and list comprehensions / > generator expressions :p > > No, really: they *are* great ;D > > Regards, > Michael They are great, and because of that greatness some of us, including me, tend to use them where there's no point doing so. I would never state that decorators are useless anyway, not in this list, I value my life too much :-) (I'll give it a try in the perl list) JM
From: Alf P. Steinbach on 4 Mar 2010 13:37 * 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
From: Mike Kent on 4 Mar 2010 15:48 On Mar 3, 10:56 am, "Alf P. Steinbach" <al...(a)start.no> wrote: > * Mike Kent: > > > What's the compelling use case for this vs. a simple try/finally? > > if you thought about it you would mean a simple "try/else". "finally" is always > executed. which is incorrect for cleanup > > by the way, that's one advantage: > > a "with Cleanup" is difficult to get wrong, while a "try" is easy to get wrong, > as you did here > > --- > > another general advantage is as for the 'with' statement generally > > > original_dir = os.getcwd() > > try: > > os.chdir(somewhere) > > # Do other stuff > > also, the "do other stuff" can be a lot of code > > and also, with more than one action the try-else introduces a lot of nesting > > > finally: > > os.chdir(original_dir) > > # Do other cleanup > > cheers & hth., > > - alf Wrong? In what way is my example wrong? It cleanly makes sure that the current working directory is the same after the try/finally as it was before it. Suboptimal, perhaps, in that the chdir in the finally part is always executed, even if the chdir in the try part failed to change the working directory. That is a clear advantage to the code you presented, in that you have the ability to register an 'undo' function only if the 'do' code succeeded. Your code also avoids a problem with many nested try/ finally blocks. But for the simple chdir example you gave, I think 'wrong' isn't the word you were looking for regarding the try/finally example I gave. Anyway, I'll keep your code in mind the next time I want to avoid a bunch of nested try/finally blocks.
From: Mike Kent on 4 Mar 2010 16:12
On Mar 4, 12:30 pm, Robert Kern <robert.k...(a)gmail.com> wrote: > He's ignorant of the use cases of the with: statement, true. <humor> Ouch! Ignorant of the use cases of the with statement, am I? Odd, I use it all the time. </humor> > Given only your > example of the with: statement, it is hard to fault him for thinking that try: > finally: wouldn't suffice. <humor> Damn me with faint praise, will you? </humor> I'm kinda amazed at the drama my innocent request for the use case elicited. From what I've gotten so far from this thread, for the actual example Mr. Steinbach used, the only disadvantage to my counter- example using try/finally is that the chdir in the finally part will always be executed, even if the chdir in the try part did not succeed. I concede that, and was aware of it when I wrote it. For the simple example given, I did not consider it compelling. A more complex example, that would have required multiple, nested try/finally blocks, would show the advantages of Mr Steinbach's recipe more clearly. However, I fail to understand his response that I must have meant try/ else instead, as this, as Mr. Kern pointed out, is invalid syntax. Perhaps Mr. Steinbach would like to give an example? |