Tim
Tim

Reputation: 99418

Evaluate and assign expression in or before with statement

If I am correct, with statement doesn't introduce a local scope for the with statement.

These are examples from Learning Python:

with open(r'C:\misc\data') as myfile:
    for line in myfile:
        print(line)
        ...more code here...

and

lock = threading.Lock()                        # After: import threading
with lock:
    # critical section of code
    ...access shared resources...

Is the second example equivalent to the following rewritten in a way similar to the first example?

with threading.Lock() as lock:
    # critical section of code
    ...access shared resources...

What are their differences?

Is the first example equivalent to the following rewritten in a way similar to the second example?

myfile = open(r'C:\misc\data')
with myfile:
    for line in myfile:
        print(line)
        ...more code here...

What are their differences?

Upvotes: 3

Views: 70

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1121734

When with enters a context, it calls a hook on the context manager object, called __enter__, and the return value of that hook can optionally be assigned to a name using as <name>. Many context managers return self from their __enter__ hook. If they do, then you can indeed take your pick between creating the context manager on a separate line or capturing the object with as.

Out of your two examples, only the file object returned from open() has an __enter__ hook that returns self. For threading.Lock(), __enter__ returns the same value as Lock.acquire(), so a boolean, not the lock object itself.

You'll need to look for explicit documentation that confirms this; this is not always that clear however. For Lock objects, the relevant section of the documentation states:

All of the objects provided by this module that have acquire() and release() methods can be used as context managers for a with statement. The acquire() method will be called when the block is entered, and release() will be called when the block is exited.

and for file objects, the IOBase documentation is rather on the vague side and you have to infer from the example that the file object is returned.

The main thing to take away is that returning self is not mandatory, nor is it always desired. Context managers are entirely free to return something else. For example, many database connection objects are context managers that let you manage the transaction (roll back or commit automatically, depending on whether or not there was an exception), where entering returns a new cursor object bound to the connection.

To be explicit:

  • for your open() example, the two examples are for all intents and purposes exactly the same. Both call open(), and if that does not raise an exception, you end up with a reference to that file object named myfile. In both cases the file object will be closed after the with statement is done. The name continues to exist after the with statement is done.

    There is a difference, but it is mostly technical. For with open(...) as myfile:, the file object is created, has it's __enter__ method called and then myfile is bound. For the myfile = open(...) case, myfile is bound first, __enter__ called later.

  • For your with threading.Lock() as lock: example, using as lock will set lock to a True (locking always either succeeds or blocks indefinitely this way). This differs from the lock = threading.Lock() case, where lock is bound to the lock object.

Upvotes: 4

RagingRoosevelt
RagingRoosevelt

Reputation: 2154

Here's a good explanation. I'll paraphrase the key part:

The with statement could be thought of like this code:

set things up
try:
    do something
finally:
    tear things down

Here, “set things up” could be opening a file, or acquiring some sort of external resource, and “tear things down” would then be closing the file, or releasing or removing the resource. The try-finally construct guarantees that the “tear things down” part is always executed, even if the code that does the work doesn’t finish.

Upvotes: 1

Related Questions