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?
with
block, that's fine - see also e.g.contextlib.suppress
. Strictly, you could dowith open(...): ...
, although as you then can't access the file handler there isn't much point! – Encasement