Meaning of "with" statement without "as" keyword
Asked Answered
C

2

57

I'm familiar with using python's with statement as a means of ensuring finalization of an object in the event of an exception being thrown. This usually looks like

with file.open('myfile.txt') as f:
    do stuff...

which is short-hand for

f = file.open('myfile.txt'):
try:
    do stuff...
finally:
    f.close()

or whatever other finalization routine a class may present.

I recently came across a piece of code dealing with OpenGL that presented this:

with self.shader:
    (Many OpenGL commands)

Note that absence of any as keyword. Does this indicate that the __enter__ and __exit__ methods of the class are still to be called, but that the object is never explicitly used in the block (i.e., it works through globals or implicit references)? Or is there some other meaning that is eluding me?

Confab answered 13/10, 2014 at 14:46 Comment(1)
If you don't need to alias the context manager inside the with block, that's fine - see also e.g. contextlib.suppress. Strictly, you could do with open(...): ..., although as you then can't access the file handler there isn't much point!Encasement
M
61

The context manager can optionally return an object, to be assigned to the identifier named by as. And it is the object returned by the __enter__ method that is assigned by as, not necessarily the context manager itself.

Using as <identifier> helps when you create a new object, like the open() call does, but not all context managers are created just for the context. They can be reusable and have already been created, for example.

Take a database connection. You create the database connection just once, but many database adapters let you use the connection as a context manager; enter the context and a transaction is started, exit it and the transaction is either committed (on success), or rolled back (when there is an exception):

with db_connection:
    # do something to the database

No new objects need to be created here, the context is entered with db_connection.__enter__() and exited again with db_connection.__exit__(), but we already have a reference to the connection object.

Now, it could be that the connection object produces a cursor object when you enter. Now it makes sense to assign that cursor object in a local name:

with db_connection as cursor:
    # use cursor to make changes to the database

db_connection still wasn't called here, it already existed before, and we already have a reference to it. But whatever db_connection.__enter__() produced is now assigned to cursor and can be used from there on out.

This is what happens with file objects; open() returns a file object, and fileobject.__enter__() returns the file object itself, so you can use the open() call in a with statement and assign a reference to the newly created object in one step, rather than two. Without that little trick, you'd have to use:

f = open('myfile.txt')
with f:
    # use `f` in the block

Applying all this to your shader example; you already have a reference to self.shader. It is quite probable that self.shader.__enter__() returns a reference to self.shader again, but since you already have a perfectly serviceable reference, why create a new local for that?

Mho answered 13/10, 2014 at 14:50 Comment(1)
Thank you for the explaining, but what is the use of the examples? After the with, the object is closed and can't be reused. It seems to me a more real world scenario the example in Edmund's Echo's answerVisage
S
5

The above answer is nicely put.

The only thing I kept asking myself while reading it, is where is the confirmation of the following scenario. In the event there is an assignment in the body of the context of the with statement, anything on the right side of the assignment is first "bound" to the context. So, in the following:

with db_connection():
   result = select(...)

... select is ~ ref_to_connection.select(...)

I put this here for anyone like me who comes and goes between languages and might benefit by a quick reminder of how to read and track the refs here.

Stalker answered 27/5, 2021 at 19:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.