RE: [Python-Dev] With statement
From: Alex Martelli [mailto:aleax@aleax.it]
To put it another way: is the advantage of:
f = open("whatever") with autoclose(f): # Use f
sufficient with respect to the existing alternative:
f = open("whatever") try: # Use f finally: f.close()
to be worth introducing a new kind of statement? I see 3 lines (4 if you insist on breaking after "finally:" of course) -- plus the body "# Use f" of course -- with the existing alternative, versus two with the new statement.
In line-counting terms, you're completely right. The advantage to me is in terms of readability/maintainability/understandability (where's a thesaurus when you need one?) and code reuse. Specifically, the try...finally case separates the open from the cleanup. When reading the code, you need to scan over the "use f" code (which may be many lines - often enough that visually matching indentation could be an issue) to see the lifetime of the file object (and check it's correct). Also, by suitable use of class names (that thesaurus again!), you can make the intent very obvious (autoclose isn't bad, but I suspect in more complex cases you could do even better). As for code reuse, surely that's obvious? A single autoclose class can be used throughout an application. It doesn't save much in terms of lines, but when you decide you want to log file closes (contrived example, I know) there's just one place to change. And of course, autoclose is the absolute minimal example, which affects both the code reuse argument (positively) and the lines of code argument (negatively). The with version also avoids the common (at least with me) mistake of try: f = open("file") # Use f finally: f.close()
With name binding,
with f = autoclosedfile("whatever"): # Use f
we're down to 1, and in this case the advantage strikes me as substantial.
But this uses a different approach entirely. Here, autoclosedfile is a self-managing file subclass, whereas the autoclose() example used an independent manager class. I know the point is subtle to the extent of being pretty irrelevant when you're looking at file objects, but it may be major in a serious application. (I'm imagining some form of application server class, part of a complex application, with significant shutdown requirements. Separation of the shutdown management functionality from the server class may make good sense. And I hate handwaving arguments like this as much as anyone else - *please* someone give me a decent use case!!!!!)
It would also be more natural for people who are used to coding "resource acquisition is initialization" in C++, since in that case, too, a name is always required (whether you use it or not in the following block):
{ autoclosedfile f("whatever"); // Use f }
But there are other subtle differences - many resources can be acquired in the one block, often not at the start of the block either. Whatever way you play it, "with" is a weak substitute. I'm *so* close to deciding that the with statement isn't worth having...
If anyone feels a need for more than this, please supply a use case - I can't think of anything concrete. Otherwise, this is what I'm going to put in the PEP...
As long as you mention in the PEP the ardent wish of some of us for name binding within 'with', np -- I can't see any other "use case" for binding-within-with save for the fact that it seems such a frequent need, and almost necessary to make 'with' enough of a win to justify its existence;-).
That one is telling :-) I'll certainly include all the options in the PEP. What really kills it for me is the fact that personally, I like the no-assignment cases for locks: class Lock: # Whatever you need here def __enter__(self): self.acquire() def __exit__(self): self.release() ... Lock GIL ... with GIL: # Do something ... with GIL: # Do something else That to me looks like the canonical use for locking. Unfortunately, people have made the point that having an *optional* assignment is hard (I'm not qualified to judge how hard, but I do know that Guido values parser simplicity). Given this, and given that I, personally, find the difference beteween with f = autocloser("file"): ... and f = open("file") with autoclose(f): # I still think there's a better name than autoclose! ... pretty minimal, I'd prefer to go with no assignment over mandatory assignment. But there's no doubt that a lot of this is opinions rather than hard fact. Paul.
"Moore, Paul" <Paul.Moore@atosorigin.com> writes:
That to me looks like the canonical use for locking. Unfortunately, people have made the point that having an *optional* assignment is hard (I'm not qualified to judge how hard, but I do know that Guido values parser simplicity).
Actually, Greg's point that you can sort it out in the compiler is a good one. Cheers, M. --
say-hi-to-the-flying-pink-elephants-for-me-ly y'rs, No way, the flying pink elephants are carrying MACHINE GUNS! Aiiee!! Time for a kinder, gentler hallucinogen... -- Barry Warsaw & Greg Ward, python-dev
On Tuesday 04 February 2003 03:00 pm, Moore, Paul wrote: ...
The with version also avoids the common (at least with me) mistake of
try: f = open("file") # Use f finally: f.close()
Yes, it IS a common mistake, and well worth avoiding, true.
With name binding,
with f = autoclosedfile("whatever"): # Use f
we're down to 1, and in this case the advantage strikes me as substantial.
But this uses a different approach entirely. Here, autoclosedfile is a self-managing file subclass, whereas the autoclose() example used an independent manager class. I know the point is subtle to the extent of
Then code it as: with f = autoclosedfile(open("whatever")): and have your "independent manager class", no problem. I'd rather have the imc in question "wrap" the file anyway (delegating to the file object any getattr it doesn't know about), but it's no big deal if you want to do it otherwise, with an explicit f.thefile or whatever.
It would also be more natural for people who are used to coding "resource acquisition is initialization" in C++, since in that case, too, a name is always required (whether you use it or not in the following block):
{ autoclosedfile f("whatever"); // Use f }
But there are other subtle differences - many resources can be acquired in the one block, often not at the start of the block either. Whatever way you play it, "with" is a weak substitute.
C++ doesn't require syntactically explicit nesting of such "blocks" (nor braces for them) but semantically the nesting IS there anyway. E.g.: { oneresource a; fooble(a); anotherone b; grepze(a, b); } if fooble(a) throws (==raises), a's destructor runs but not b's (nor b's constructor). When teaching C++ I always explained this kind of thing as fully equivalent to: { oneresource a; fooble(a); { anotherone b; grepze(a, b); } } and I saw MANY eyes light up in "ah, FINALLY I understand it!" ways when I showed this equivalence. So, I disagree that "with" (WITH binding ability) is a weak substitute -- it's "explicit is better than implicit" in term of "nesting", but that's not necessarily a weakness. WITHOUT binding abilty, then yes, "with" is arguably (I'm not sure...) too weak to be worth having.
What really kills it for me is the fact that personally, I like the no-assignment cases for locks:
But that doesn't work in C++ -- there HAS to be a name binding for a RAI -- and I think that, if the name binding is either forbidden or mandatory (i.e. too hard to make optional), C++ has it right here -- mandatory is better than forbidden.
class Lock: # Whatever you need here def __enter__(self): self.acquire() def __exit__(self): self.release() ...
Lock GIL
meaning GIL = Lock() I guess? Too much C++...;-)
with GIL: # Do something
...
with GIL: # Do something else
That to me looks like the canonical use for locking. Unfortunately, people
It is, but the price of writing "with GIL=GIL:" or whatever is pretty minor, and quite comparable to the "canonical use for repetition" of having to bind a name to the for-loop's iteration variable. Alex
participants (3)
-
Alex Martelli -
Michael Hudson -
Moore, Paul