18

I saw this question, and I understand when you would want to use with foo() as bar:, but I don't understand when you would just want to do:

bar = foo()
with bar:
   ....

Doesn't that just remove the tear-down benefits of using with ... as, or am I misunderstanding what is happening? Why would someone want to use just with?

2
  • You probably don't want to do with foo: in this case. foo is a callable, and the result of that callable will almost certainly be the context manager you want to exit, not the callable itself. But with bar: is perfectly reasonable here, as in @freakish's answer. Commented Jun 11, 2013 at 20:30
  • You are right; I changed it. Commented Jun 11, 2013 at 20:31

3 Answers 3

12

To expand a bit on @freakish's answer, with guarantees entry into and then exit from a "context". What the heck is a context? Well, it's "whatever the thing you're with-ing makes it". Some obvious ones are:

  • locks: you take a lock, manipulate some data, and release the lock.
  • external files/streams: you open a file, read or write it, and close it.
  • database records: you find a record (which generally also locks it), use some fields and maybe change them, and release the record (which also unlocks it).

Less obvious ones might even include certain kinds of exception trapping: you might catch divide by zero, do some arithmetic, and then stop catching it. Of course that's built into the Python syntax: try ... except as a block! And, in fact, with is simply a special case of Python's try/except/finally mechanisms (technically, try/finally wrapped around another try block; see comments).

The as part of a with block is useful when the context-entry provides some value(s) that you want to use inside the block. In the case of the file or database record, it's obvious that you need the newly-opened stream, or the just-obtained record. In the case of catching an exception, or holding a lock on a data structure, there may be no need to obtain a value from the context-entry.

Sign up to request clarification or add additional context in comments.

7 Comments

with isn't really a special case of try/except, either conceptually or in reality. It's closer to a special case of try/finally, but even that isn't really accurate. (It's actually a try/except/else inside a try/finally, with at least two explicit calls to __exit__ in different places.)
@abarnert: true; I was trying to simplify, and maybe over-simplified. :-)
But I think it's a little misleading this way. It's the finally that's the key part to what a with block intuitively does, not the except. (BTW, I wish whoever downvoted this would have added a comment, because I don't think my comment is enough justification for a vote, and if there's a different problem it's worth actually raising it so the answer can be improved, instead of drive-by voting displeasure.)
Ah, I think part of the issue here is that I "think of" the complete construct (try ... except ... else ... finally) as a "try/except" block. That is, for me, the label using two of the four keywords identifies the entire construct. But it is ambiguous and obviously others will interpret it differently (which is why I added the "technically" part after your earlier comment).
But you can't actually implement it as a single try…except…else…finally construct. And the inner try…except is basically just an implementation detail to make uncommon cases work better. The outer try…finally is the whole point. See pastebin.com/LQV9HPWZ for an explanation with formatted code.
|
5

For example when you want to use Lock():

from threading import Lock
myLock = Lock()
with myLock:
   ...

You don't really need the Lock() object. You just need to know that it is on.

9 Comments

I think you mean "You don't really need the Lock object."
@martineau: It may be a bit confusing, but I don't think it's wrong or misleading. What could "the Lock() object" mean but "the object returned by the Lock() call"?
@abarnert: Exactly, you do need the returned object for the with, not the Lock object (class) itself. Which is way I think what was said is wrong.
@martineau: In Python, Lock() doesn't refer to the Lock class, it refers to an instance of the Lock class. Why would you expect the opposite to be true in English? I agree that it could be clearer—e.g., just say "the myLock object"—but I don't think it's wrong as-is.
@abarnert: OK, Lock() isn't actually a class, it's a factory function that returns a thread.lock object which is very similar usage-wise. That's why I objected to the statement in the answer saying you really didn't need it -- meaning the value it returns -- since you really do, for example, for use in a with statement or to manually call its acquire() and release() methods.
|
4

Using with without as still gets you the exact same teardown; it just doesn't get you a new local object representing the context.

The reason you want this is that sometimes the context itself isn't directly useful—in other words, you're only using it for the side effects of its context enter and exit.

For example, with a Lock object, you have to already have the object for the with block to be useful—so, even if you need it within the block, there's no reason to rebind it to another name. The same is true when you use contextlib.closing on an object that isn't a context manager—you already have the object itself, so who cares what closing yields?

With something like sh.sudo, there isn't even an object that you'd have any use for, period.

There are also cases where the point of the context manager is just there to stash and auto-restore some state. For example, you might want to write a termios.tcsetattr-stasher, so you can call tty.setraw() inside the block. You don't care what the stash object looks like, all you care about is that it gets auto-restored.

decimal.localcontext can work in any of these ways—you can pass it an object you already have (and therefore don't need a new name for), or pass it an unnamed temporary object, or have it just stash the current context to be auto-restored. But in any of those cases.

There are some hybrid cases where sometimes you want the context, sometimes you don't. For example, if you just want a database transaction to auto-commit, you might write with autocommit(db.begin()):, because you aren't going to access it inside the block. But if you want it to auto-rollback unless you explicitly commit it, you'd probably write with autorollback(db.begin()) as trans:, so you can trans.commit() inside the block. (Of course often, you'd actually want a transaction that commits on normal exit and rolls back on exception, as in PEP 343's transaction example. But I couldn't think of a better hybrid example here…)

PEP 343 and its predecessors (PEP 310, PEP 340, and other things linked from 343) explains all of this to some extent, but it's understandable that you wouldn't pick that up on a casual read—there's so much information that isn't relevant, and it mainly just explains the mile-high overview and then the implementation-level details, skipping over everything in between.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.