[Python-Dev] With statement

Moore, Paul Paul.Moore@atosorigin.com
Tue, 4 Feb 2003 14:00:47 -0000


From: Alex Martelli [mailto:aleax@aleax.it]
> To put it another way: is the advantage of:
>=20
>     f =3D open("whatever")
>     with autoclose(f):
>         # Use f
>=20
> sufficient with respect to the existing alternative:
>=20
>     f =3D open("whatever")
>     try:
>         # Use f
>     finally: f.close()
>=20
> 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 =3D open("file")
	# Use f
    finally:
        f.close()

> With name binding,
>=20
>     with f =3D autoclosedfile("whatever"):
>         # Use f
>=20
> 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):
>=20
> {   =20
>     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 =3D autocloser("file"):
        ...

and

    f =3D 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.