Movatterモバイル変換


[0]ホーム

URL:


— FREE Email Series —

🐍 Python Tricks 💌

Python Tricks Dictionary Merge

🔒 No spam. Unsubscribe any time.

Browse TopicsGuided Learning Paths
Basics Intermediate Advanced
aialgorithmsapibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnewsnumpyprojectspythonstdlibtestingtoolsweb-devweb-scraping

Table of Contents

Recommended Course

Context Managers and Python's with Statement

Context Managers and Using Python's with Statement

34m · 8 lessons

Python's with Statement: Manage External Resources Safely

Python's with Statement: Manage External Resources Safely

byLeodanis Pozo RamosPublication date Aug 13, 2025Reading time estimate 39mintermediatepython

Table of Contents

Remove ads

Recommended Course

Context Managers and Using Python's with Statement(34m)

Python’swith statement allows you to manage external resources safely by using objects that support the context manager protocol. These objects automatically handle the setup and cleanup phases of common operations.

By using thewith statement alongside appropriate context managers, you can focus on your core logic while the context managers prevent resource leaks like unclosed files, unreleased memory, or dangling network connections.

By the end of this tutorial, you’ll understand that:

  • Python’swith statement automates the process of setting up and tearing down computational resources using context managers.
  • Usingwith reduces code complexity andprevents resource leaks by ensuring proper resource release, even if exceptions occur.
  • Acontext manager in Python is an object that implements.__enter__() and.__exit__() methods to manage resources safely.

Get ready to learn how Python’swith statement and context managers streamline the setup and teardown phases of resource management so you can write safer, more reliable code.

Get Your Code:Click here to download the free sample code that shows you how to use Python’s with statement to manage external resources safely.

Take the Quiz: Test your knowledge with our interactive “Context Managers and Python's with Statement” quiz. You’ll receive a score upon completion to help you track your learning progress:


Context Managers and Python's with Statement

Interactive Quiz

Context Managers and Python's with Statement

Test your knowledge of Python's with statement and context managers to write cleaner code and manage resources safely and efficiently.

Managing External Resources in Python

Properly managingexternal resources, such asfiles,locks, and network connections, is a common requirement in programming. Sometimes, a program uses a given resource and doesn’t release the associated memory when it no longer needs the resource. This kind of issue is called amemory leak because the available memory shrinks every time you create a new instance of a resource without releasing the unneeded ones.

Managing resources properly is often a tricky task. It requiressetup andteardown phases. The latter phase requires you to perform cleanup actions, likeclosing a file, releasing a lock, or closing a network connection. If you forget to perform these cleanup actions, then your application keeps the resource occupied. This behavior might compromise valuable system resources, such as memory and network bandwidth.

For example, say that a program that uses databases keeps creating new connections without releasing the old ones or reusing them. In that case, the databaseback end can stop accepting new connections. This might require an administrator to log in and manually terminate those stale connections to make the database usable again.

Another common issue occurs when developers work with files.Writing text to files is usually abuffered operation. This means that calling.write() on a file won’t immediately result in writing text to the physical file, but to a temporary buffer. Sometimes, when the buffer isn’t full, developers forget to call.close() and part of the data can be lost.

Another possibility is that your application runs into errors orexceptions that cause thecontrol flow to bypass the code responsible for releasing the resource at hand. Here’s an example where you use the built-inopen() function to write some text to a file:

Python
file=open("hello.txt","w")file.write("Hello, World!")file.close()

This code doesn’t guarantee the file will be closed if an exception occurs during the call to.write(). In this situation, the code might never call.close(), and your program will leak a file descriptor. Failing to release a file descriptor on some operating systems can prevent other programs from accessing the underlying file.

Note: To learn more about closing files, check out theWhy Is It Important to Close Files in Python? tutorial.

In Python, you can use a couple of general approaches to deal with resource management. You can wrap your code in:

  1. Atryfinally construct
  2. Awith construct

The first approach is quite generic and allows you to provide setup and teardown code to manage any kind of resource. However, it’s a little bit verbose, and you might forget some cleanup actions if you use this construct in several places.

The second approach provides a straightforward way to provide and reuse setup and teardown code. In this case, you’ll have the limitation that thewith statement only works withcontext managers. In the next two sections, you’ll learn how to use both approaches in your code.

Thetryfinally Construct

Working with files is probably the most common example of resource management in programming. In Python, you can use atryfinally construct to handle opening and closing files properly:

Python
file=open("hello.txt","w")try:file.write("Hello, World!")finally:file.close()

In this example, you open thehello.txt file usingopen(). To write some text into the file, you wrap the call to.write() in atry statement with afinally clause. This clause guarantees that the file is properly closed by calling.close(), even if an exception occurs during the call to.write() in thetry clause. Remember that thefinally clause always runs.

When managing external resources in Python, you can use the construct in the previous example to handle setup and teardown logic. The setup logic might include opening the file and writing content to it, while the teardown logic might consist of closing the file to release the acquired resources.

Thetry block in the example above can potentially raise exceptions, such asAttributeError orNameError. You can handle these exceptions with anexcept clause, as shown in the following example:

Python
file=open("hello.txt","w")try:file.write("Hello, World!")exceptExceptionase:print(f"An error occurred while writing to the file:{e}")finally:file.close()

In this example, you use theException class to catch exceptions that can occur while writing to the file. Then, you print an error message to inform the user. Thefinally clause always runs so that the file is closed correctly.

Note: In Python, you should use specificexceptions instead of the generic ones, likeException, to handle errors. Otherwise, you could be catching errors that you didn’t expect to happen, and this can lead to silent bugs.

Even though thetryfinally construct works correctly, Python has a more compact and elegant solution for you: thewith statement.

Thewith Statement

The Pythonwith statement creates aruntime context that allows you to execute a code block under the control of acontext manager.PEP 343 added thewith statement to make it possible to factor out common use cases of thetryfinally construct.

Compared to the traditionaltryfinally construct, thewith statement makes your code clearer, safer, and more readable. Many classes and objects in thestandard library support thewith statement by implementing the context managerprotocol (special methods). For example, when you callopen(), you get afile object that supports this protocol and, therefore, thewith statement.

To write awith statement, you need to use the following general syntax:

Python Syntax
withexpression[asobj]:<block>

The context manager object results from evaluating theexpression after thewith keyword. In other words,expression must return an object that implements the context management protocol. This protocol consists of twospecial methods:

  1. .__enter__() is called by thewith statement to enter the runtime context.
  2. .__exit__() is called when the execution leaves thewith code block.

Theas specifier is optional. If you provide anobj variable withas, then thereturn value of calling.__enter__() on the context manager object is bound to that variable.

Note: Some context managers returnNone from.__enter__() because they have no useful object to give back to the caller. In these situations, specifying anobj variable makes no sense.

Here’s how thewith statement works internally:

  1. Executeexpression to obtain a context manager object.
  2. Call.__enter__() on the context manager and bind its return value toobj if provided.
  3. Execute thewith code block.
  4. Call.__exit__() on the context manager when thewith code block finishes.

The.__enter__() method typically provides thesetup logic. Thewith statement is acompound statement that starts a code block, like other compound statements, such asconditional statements andfor loops.

Inside this code block, you can run one or more statements. Typically, you use thewith code block to manipulateobj if applicable.

Once thewith code block finishes, Python automatically calls.__exit__(). This method typically provides theteardown logic or cleanup code, such as calling.close() on an open file object. That’s why thewith statement is so useful. It makes properly acquiring and releasing resources a breeze.

Here’s how to open yourhello.txt file for writing using thewith statement:

Python
withopen("hello.txt",mode="w",encoding="utf-8")asfile:file.write("Hello, World!")

When you run thiswith statement,open() returns anio.TextIOBase object. This object supports the context manager protocol, so thewith statement calls.__enter__() and assigns its return value tofile. Then, you can manipulate the file inside thewith code block. When the block ends, Python automatically calls.__exit__(), which closes the file for you, even if an exception occurs in thewith code block.

Thiswith construct is shorter than itstryfinally equivalent construct, but it’s also less generic because you can only use thewith statement with objects that support the context management protocol. In contrast,tryfinally allows you to perform cleanup actions for any object, even if it doesn’t implement the context management protocol.

Thewith statement also supportsmultiple context managers. You can supply any number of context managers separated by commas:

Python Syntax
withA()asa,B()asb:<block>

This works like nestedwith statements, but without nesting. It might be useful when you need to open two files at a time—the first for reading and the second for writing:

Python
withopen("hello.txt")asin_file,open("output.txt","w")asout_file:forlineinin_file:out_file.write(line.upper())

In this example, you read and transform the content ofhello.txt line by line. Then, you write each processed line tooutput.txt within the same code block.

Thewith statement can make the code that deals with system resources more readable, concise, and safer. It helps avoid resource leaks by making it almost impossible to forget to clean up, close, and release resources after you’re done with them.

Usingwith allows you to abstract away most of the resource handling logic. Instead of using explicittryfinally constructs with setup and teardown logic, you can pack this logic into a context manager and handle it using thewith statement to avoid repetition.

Using Python’swith Statement

Thewith statement is useful in several common situations. Several objects in Python’s standard library provide support for the context manager protocol, so you can use them inwith statements for multiple tasks that imply setup and teardown logic.

In the following sections, you’ll code some examples that show how to use thewith statement with built-in objects, standard-library classes, and third-party libraries.

Working With Files

So far, you’ve usedopen() to manipulate files in awith construct. Managing files this way is generally recommended because it ensures that openedfile descriptors are automatically closed after the flow of execution leaves thewith code block.

Again, a common way to open a file in awith statement is through theopen() function:

Python
withopen("hello.txt",mode="w",encoding="utf-8")asfile:file.write("Hello, World!")

In this example, the context manager closes the file after leaving thewith code block. A common mistake you might encounter is shown below:

Python
>>>file=open("hello.txt",mode="w",encoding="utf-8")>>>withfile:...file.write("Hello, World!")...13>>>withfile:...file.write("Welcome to Real Python!")...Traceback (most recent call last):...ValueError:I/O operation on closed file.

The firstwith successfully writes"Hello, World!" intohello.txt. Note that.write() returns the number of bytes written into the file—13 bytes in this example. When you try to run a secondwith, you get aValueError because the file is already closed.

Another way to use thewith statement to open and manage files is by using the.open() method on aPath object:

Python
>>>importpathlib>>>file_path=pathlib.Path("hello.txt")>>>withfile_path.open("w",encoding="utf-8")asfile:...file.write("Hello, World!")...13

Path is a class that represents concrete paths to physical files on your computer. Calling.open() on aPath object that points to a physical file opens it just likeopen() would. So,Path.open() works similarly toopen(), but the file path is automatically provided by thePath object you call the method on.

Thepathlib package provides an elegant, straightforward, andPythonic way to manipulate file system paths. You should consider usingPath.open() in yourwith statements as a best practice in Python.

Finally, whenever you load an external file, your program should check for possible issues, such as a missing file, writing and reading access, and so on. Here’s a pattern that you should consider using when working with files:

Python
>>>importpathlib>>>importlogging>>>file_path=pathlib.Path("/hello.txt")>>>try:...withfile_path.open(mode="w")asfile:...file.write("Hello, World!")...exceptOSErroraserror:...logging.error("Writing to file%s failed due to:%s",file_path,error)...ERROR:root:Writing to file /hello.txt failed due to: [Errno 13]⮑ Permission denied: '/hello.txt'

In this example, you wrap thewith statement in atryexcept block. If anOSError occurs during the execution ofwith, then you use thelogging module to log the error with a user-friendly and descriptive message.

Traversing Directories

Theos module provides a function calledscandir() that returns aniterator overos.DirEntry objects corresponding to the entries in a given directory. This function is specially designed to provide optimal performance when you’re traversing a directory structure.

A call toscandir() with a directory path as an argument returns an iterator that supports the context management protocol:

Python
>>>importos>>>withos.scandir(".")asentries:...forentryinentries:...print(entry.name,"->",entry.stat().st_size,"bytes")...Documents -> 4096 bytesVideos -> 12288 bytesDesktop -> 4096 bytesDevSpace -> 4096 bytes.profile -> 807 bytesTemplates -> 4096 bytesPictures -> 12288 bytesPublic -> 4096 bytesDownloads -> 4096 bytes

In this example, you write awith statement withos.scandir() as the context manager supplier. Then, you iterate over the entries in theworking directory represented by"." andprint their names and sizes on the screen. In this case,.__exit__() callsscandir.close() to close the iterator and release the acquired resources.

Note that if you run this example on your machine, you’ll get a different output depending on the content of your target directory.

Performing High-Precision Calculations

Thedecimal module provides a handy way to tweak the precision to use in a given calculation that involvesdecimal numbers. The precision defaults to28 places, but you can change it to meet your specific requirements.

A quick way to perform calculations with a custom precision is usinglocalcontext() fromdecimal:

Python
>>>fromdecimalimportDecimal,localcontext>>>withlocalcontext(prec=42):...Decimal("1")/Decimal("42")...Decimal('0.0238095238095238095238095238095238095238095')>>>Decimal("1")/Decimal("42")Decimal('0.02380952380952380952380952381')

Here,localcontext() returns a context manager that creates a context allowing you to perform calculations using a custom precision. In thewith code block, you have a precision of42 places. When thewith code block finishes, the precision is back to its default value of28 places.

Handling Locks in Multithreaded Programs

Thethreading.Lock class in the Python standard library also supports thewith statement. This class provides a primitive lock to prevent multiple threads from modifying a shared resource at the same time in amultithreaded application.

You can use aLock object in awith statement to automatically acquire and release a given lock. For example, say you need to protect the balance of a bank account:

Python
importthreadingbalance_lock=threading.Lock()# Use the try ... finally patternbalance_lock.acquire()try:# Update the account balance here ...finally:balance_lock.release()# Use the with patternwithbalance_lock:# Update the account balance here ...

Thewith statement in the second example automatically acquires and releases a lock when the flow of execution enters and leaves the statement. This way, you can focus on what really matters in your code and forget about those repetitive operations.

In this example, the lock in thewith statement creates a protected region known as thecritical section, which prevents concurrent access to the account balance.

Testing for Exceptions With pytest

So far, you’ve coded a few examples using context managers that are available in the Python standard library. However, several third-party libraries include objects that also support the context management protocol.

Say you’retesting your code withpytest. Some of your functions and code blocks raise exceptions under certain situations, and you want to test those cases. To do that, you can usepytest.raises(), which allows you to assert that a block of code or a function call raises a specific exception.

Thepytest.raises() function returns a context manager, so you can use that object in awith statement:

Python
>>>importpytest>>>1/0Traceback (most recent call last):...ZeroDivisionError:division by zero>>>withpytest.raises(ZeroDivisionError):...1/0...>>>favorites={"fruit":"apple","pet":"dog"}>>>favorites["car"]Traceback (most recent call last):...KeyError:'car'>>>withpytest.raises(KeyError):...favorites["car"]...

In the first example, you usepytest.raises() to capture theZeroDivisionError exception that the expression1 / 0 raises. The second example uses this function to capture theKeyError that’s raised when you access a key that doesn’t exist in a given dictionary.

Note: When writing unit tests withPython’sunittest module, you can use thepatch() function as a context manager to temporarily replace objects during testing. This technique is calledmocking and is useful for isolating your code from external dependencies.

If your function or code block doesn’t raise the expected exception, thenpytest.raises() raises a failure exception:

Python
>>>withpytest.raises(ZeroDivisionError):...4/2...2.0Traceback (most recent call last):...Failed:DID NOT RAISE <class 'ZeroDivisionError'>

Another cool feature ofpytest.raises() is that you can specify a target variable to inspect the raised exception. For example, if you want to verify the error message, then you can do something like this:

Python
>>>withpytest.raises(ZeroDivisionError)asexc:...1/0...>>>assertstr(exc.value)=="division by zero"

You can use all thesepytest.raises() features to capture the exceptions you raise from your functions and code blocks. This is a cool and useful tool that you can incorporate into your current testing strategy.

Summarizing thewith Statement’s Advantages

To summarize what you’ve learned so far, here’s a non-exhaustive list of the general benefits of using thewith statement:

  • Makesresource management safer than using equivalenttryfinally constructs
  • Encapsulates standard uses oftryfinally constructs incontext managers
  • Allows using context managers to pack the code that handlessetup andteardown logic
  • Helps preventresource leaks

Using thewith statement consistently can improve the general quality of your code and make it safer by preventing resource leak problems.

Using theasync with Statement

Thewith statement also has anasynchronous version,async with. You can use it to write context managers that work within asynchronous code. It’s quite common to seeasync with in that kind of code, as manyI/O-bound tasks involve setup and teardown phases.

For example, say you need to code an asynchronous function to check whether some websites are online. To do that, you can useaiohttp, andasyncio. Note thataiohttp is a third-party package that you need to install by runningpython -m pip install aiohttp on your command line.

Here’s a quick script that implements the required functionality:

Pythonsite_checker_v1.py
 1importasyncio 2importaiohttp 3 4asyncdefcheck(url): 5asyncwithaiohttp.ClientSession()assession: 6asyncwithsession.get(url)asresponse: 7print(f"{url}: status ->{response.status}") 8html=awaitresponse.text() 9print(f"{url}: type ->{html[:17].strip()}")1011asyncdefmain():12awaitasyncio.gather(13check("https://realpython.com"),14check("https://pycoders.com"),15)1617asyncio.run(main())

Here’s a breakdown of what this script does:

  • Line 1importsasyncio, which allows you to writeconcurrent code using theasync andawait keywords.
  • Line 2 importsaiohttp, which provides an asynchronous HTTP client and server forasyncio and Python.
  • Line 4 definescheck() as an asynchronous orcoroutine function using theasync defkeywords.

Insidecheck(), you use twoasync with statements, one nested in the other:

  • Line 5 defines an outerasync with that instantiatesaiohttp.ClientSession() to get asession object that supports the context manager protocol.
  • Line 6 defines an innerasync with statement that calls.get() on the session using a URL as an argument. This creates aresponse object that also supports the context manager protocol.
  • Line 7 prints the responsestatus code for theURL at hand.
  • Line 8 runs an awaitable call to.text() onresponse and stores the result inhtml.
  • Line 9 prints the site URL and its document type,doctype.
  • Line 11 defines the script’smain() function, which is also a coroutine function.
  • Line 12 callsgather(), which runsawaitable objects concurrently. In this example,gather() runs two instances ofcheck() with a different URL for each.
  • Line 17 runsmain() usingasyncio.run(), which creates anevent loop and closes it at the end of the operation.

If yourun this script from your command line, then you’ll get output similar to the following:

Shell
$pythonsite_checker_v1.pyhttps://realpython.com: status -> 200https://pycoders.com: status -> 200https://pycoders.com: type -> <!doctype html>https://realpython.com: type -> <!doctype html>

Cool! Your script works—both sites are currently available. You’ve also successfully retrieved the document type from each site’s home page.

Note: Your output may look slightly different due to the nondeterministic nature of concurrent task scheduling and network latency. Specifically, individual lines may appear in a different order.

Theasync with statement works like a regularwith statement, but it requires anasynchronous context manager. In other words, it needs a context manager that’s able to suspend execution in its enter and exit methods.

Asynchronous context managers implement the special methods.__aenter__() and.__aexit__(), which are the asynchronous counterparts to.__enter__() and.__exit__() in regular context managers.

When you use theasync with ctx_mgr construct, Python implicitly callsawait ctx_mgr.__aenter__() on entry andawait ctx_mgr.__aexit__() on exit. This allows asynchronous context management to work seamlessly.

Creating Custom Context Managers

You’ve already worked with context managers from the standard library and third-party libraries. There’s nothing special or magical aboutopen(),threading.Lock,decimal.localcontext(), and the others. They simply return objects that implement the context management protocol.

You can provide the same functionality by implementing the.__enter__() and.__exit__() special methods in yourclass-based context managers. You can also create customfunction-based context managers using thecontextlib.contextmanager decorator from the standard library and an appropriately codedgenerator function.

In practice, context managers and thewith statement aren’t limited to resource management. They allow you to provide and reuse common setup and teardown code. In other words, you can use a context manager to handle any pair of operations that must occurbefore andafter a task or procedure, such as:

  • Open and close
  • Lock and release
  • Change and reset
  • Create and delete
  • Enter and exit
  • Start and stop
  • Install and uninstall

You can provide code to safely manage any of these pairs of operations in a context manager. Then, you can reuse that context manager inwith statements throughout your code. This helps prevent errors, reduces repetitive boilerplate code, and makes yourAPIs safer, cleaner, and more user-friendly.

In the next two sections, you’ll learn the basics of creating class-based and function-based context managers.

Coding Class-Based Context Managers

To create aclass-based context manager, you need to add the.__enter__() and.__exit__() special methods to your class. The table below summarizes how these methods work, the arguments they take, and the kind of logic you can include:

MethodDescription
.__enter__(self)This method handles the setup logic and is called automatically when entering a new context using thewith statement. Its return value is bound to thewith target variable.
.__exit__(self, exc_type, exc_value, exc_tb)This method handles the teardown logic and is automatically called when the flow of execution leaves thewith block. If an exception occurs, thenexc_type,exc_value, andexc_tb hold the exception type, value, andtraceback information, respectively.

Next, you’ll learn more about writing your own context manager and dive a bit deeper into the implementation of these special methods.

Writing a Demo Class-Based Context Manager

When thewith statement executes, Python calls.__enter__() on the context manager object to set up the new runtime context. If you provide a target variable with theas specifier, then the return value of.__enter__() is assigned to that variable. When the flow of execution leaves the context,.__exit__() is called.

Below is a demo class-based context manager that shows how Python calls the.__enter__() and.__exit__() methods in awith construct:

Python
>>>classHelloContextManager:...def__enter__(self):...print("Entering the context...")...return"Hello, World!"......def__exit__(self,exc_type,exc_value,exc_tb):...print("Leaving the context...")...print(f"{exc_type= }")...print(f"{exc_value= }")...print(f"{exc_tb= }")...>>>withHelloContextManager()ashello:...print(hello)...Entering the context...Hello, World!Leaving the context...exc_type  = Noneexc_value = Noneexc_tb    = None

In.__enter__(), you print a message to indicate that the flow of execution is entering a new context. Then, you return the"Hello, World!" string. In.__exit__(), you print a message to signal that the execution flow is leaving the context. You also print the content of the three arguments.

When thewith statement runs, Python creates a new instance ofHelloContextManager and calls its.__enter__() method. You know this because you getEntering the context... printed on the screen. Next, Python runs thewith code block, which printshello to the screen. Note thathello holds the return value of.__enter__(), which is"Hello, World!" in this example.

When the execution flow exits thewith code block, Python calls.__exit__(). You know that because you getLeaving the context... printed on your screen. If no exception occurs in thewith code block, then the three arguments to.__exit__() are set toNone. You confirm this behavior in the final output lines.

Note: If you forget the signature of.__exit__() or don’t need to access its arguments, you can use the*args syntax in the method’s definition.

Now, what if an exception occurs during the execution of thewith block? To find out, go ahead and run the followingwith statement:

Python
>>>withHelloContextManager()ashello:...print(hello)...hello[100]...Entering the context...Hello, World!Leaving the context...exc_type  = <class 'IndexError'>exc_value = IndexError('string index out of range')exc_tb    = <traceback object at 0x7f0cebcdd080>Traceback (most recent call last):...IndexError:string index out of range

In this example, you try to retrieve the value at index100 in thestring"Hello, World!". This action raises anIndexError exception because the string doesn’t have 100 characters. Therefore, the arguments to.__exit__() are set as follows:

  • exc_type: the exception class,IndexError.
  • exc_value: the concrete exception object with a descriptive message.
  • exc_tb: the traceback object,<traceback object at 0x7f0cebcdd080>.

This behavior is especially useful when you want your context manager to handle exceptions internally.

Handling Exceptions Within Context Managers

If the.__exit__() method returnsTrue, then any exception that occurs in thewith block is swallowed and the execution continues at the next statement afterwith. If.__exit__() returnsFalse, then exceptions are propagated out of the context. This is also the default behavior when the method doesn’t return anything explicitly.

You can take advantage of these behaviors to encapsulate exception handling inside the context manager or to propagate the exceptions as needed.

As an example of encapsulating exception handling in a context manager, say you expectIndexError to be a possible exception when you’re working withHelloContextManager. You might want to handle that exception in the context manager so you don’t have to repeat the exception-handling code in everywith code block.

In that situation, you can do something like the following:

Python
>>>classHelloContextManager:...def__enter__(self):...print("Entering the context...")...return"Hello, World!"......def__exit__(self,exc_type,exc_value,exc_tb):...print("Leaving the context...")...ifisinstance(exc_value,IndexError):...# Handle IndexError here......print(f"An exception occurred in your with block:{exc_type}")...print(f"Exception message:{exc_value}")...returnTrue...>>>withHelloContextManager()ashello:...print(hello)...hello[100]...Entering the context...Hello, World!Leaving the context...An exception occurred in your with block: <class 'IndexError'>Exception message: string index out of range

In.__exit__(), you check ifexc_value is an instance ofIndexError using the built-inisinstance() function. If so, you print a couple of informative messages and finally return withTrue.

Again, returning aTrue makes it possible to swallow the exception and continue the normal execution after thewith code block.

In this example, if noIndexError occurs, then the method implicitly returnsNone, and the exception propagates out of the context. This is the desired behavior because it avoids hiding unexpected exceptions. If you want to be more explicit, then you can returnFalse outside theif block.

YourHelloContextManager class is now able to handleIndexError exceptions that occur in thewith code block. Since you returnTrue when anIndexError occurs, the execution flow continues in the next line, right after exiting thewith code block.

Opening Files for Writing

Now that you know how to implement the context management protocol, you can code a more realistic example. For instance, below is how you can create a context manager that opens files for writing directly:

Python
>>>classWritableFile:...def__init__(self,file_path):...self.file_path=file_path......def__enter__(self):...self.file_obj=open(self.file_path,mode="w")...returnself.file_obj......def__exit__(self,*_):...ifself.file_obj:...self.file_obj.close()...

WritableFile allows you to open the file for writing using the"w" mode ofopen(). You do this in the.__enter__() method. In the.__exit__() method, you close the file to release the acquired resources. Note how you use the*args syntax to maintain compatibility with the context management protocol. Since you don’t need these arguments, you use asingle underscore to suppress a potentiallinter warning about unused variables.

Here’s how you can use your context manager:

Python
>>>withWritableFile("hello.txt")asfile:...file.write("Hello, World!")...13

After running this code, you’ll have ahello.txt file containing the"Hello, World!" string. As an exercise, you can write a complementary context manager that opens files for reading, but usingpathlib functionalities. Go ahead and give it a try!

Redirecting the Standard Output to a File

A subtle detail to consider when you’re implementing the context manager protocol is that sometimes you don’t have a useful object to return from.__enter__()—and therefore nothing meaningful to assign to the target variable in thewith header. In those cases, you can explicitly returnNone, or just rely on Python’simplicit return value, which is alsoNone.

For example, say you need to temporarily redirect the standard output,sys.stdout, to a given file on your disk. To do this, you can create a context manager like the following:

Python
>>>importsys>>>classStandardOutputRedirector:...def__init__(self,new_output):...self.new_output=new_output......def__enter__(self):...self.std_output=sys.stdout...sys.stdout=self.new_output......def__exit__(self,*_):...sys.stdout=self.std_output...

This context manager takes a file object as an argument. In.__enter__(), you reassign the standard output,sys.stdout, to an instance attribute to avoid losing the reference to it. Then, you reassign the standard output to point to the file on your disk. In.__exit__(), you restore the standard output to its original value.

To useStandardOutputRedirector, you can do something like this:

Python
>>>withopen("hello.txt","w")asfile:...withStandardOutputRedirector(file):...print("Hello, World!")...print("Back to the standard output...")...Back to the standard output...

The outerwith statement provides the file object that you’ll use as your new output,hello.txt. The innerwith temporarily redirects the standard output tohello.txt, so the first call toprint() writes directly to that file instead of printing"Hello, World!" to your screen. When you leave the innerwith code block, the standard output is set back to its original value.

Note: If you’re only redirecting the output ofprint(), then you can get the same functionality without a context manager. You just need to provide afile argument toprint() as shown below:

Python
>>>withopen("hello.txt","w")asfile:...print("Hello, World!",file=file)...

In this example,print() takes yourhello.txt file as an argument. This causesprint() to write directly into the physical file on your disk instead of printing"Hello, World!".

StandardOutputRedirector is a quick example of a context manager that doesn’t have a useful value to return from.__enter__(). In this example, you rely on Python’s implicit behavior of returningNone.

Measuring Execution Time

Just like any other class, a context manager can encapsulate some internalstate. The following example shows how to create astateful context manager to measure the execution time of a given piece of code:

Python
>>>fromtimeimportperf_counter,sleep>>>classTimer:...def__enter__(self):...self.start=perf_counter()......def__exit__(self,*_):...end=perf_counter()...print(f"Elapsed time:{end-self.start:.4f} seconds")...>>>withTimer():...# The code to measure goes here......sleep(0.5)...Elapsed time: 0.5051 seconds

In the.__enter__() method, you calltime.perf_counter() to get the current timestamp at the beginning of awith code block and store it in the.start attribute. This value represents the context manager’s initial state.

Note: For more on timing your code, check outPython Timer Functions: Three Ways to Monitor Your Code.

Once thewith block ends,.__exit__() gets called. The method gets the time at the end of the block and stores the value in a local variableend. Finally, it prints a message to inform you of the elapsed time.

Indenting Text

What if the resource you wanted to manage is thetext indentation level in some kind of report generator application? For example, say that you want to write code that generatesHTML output like the following:

HTML
<div><p>    Hello, World!</p></div>

This could be a great exercise to help you understand how context managers work. So, before you check out the implementation, you might take some time and try to produce this output with the help of a context manager that you can use as shown below:

Python
withIndenter()asindenter:indenter.print("<div>")withindenter:indenter.print("<p>")withindenter:indenter.print("Hello, World!")indenter.print("</p>")indenter.print("</div>")

Notice how this code enters and leaves the same context manager multiple times to switch between different indentation levels.

Ready? Here’s how you can implement this functionality using a class-based context manager:

Python
classIndenter:def__init__(self,width=2):self.indentation=" "*widthself.level=-1def__enter__(self):self.level+=1returnselfdef__exit__(self,*_):self.level-=1defprint(self,text):print(self.indentation*self.level+text)

Here,.__enter__() increments.level by1 every time the flow of execution enters the context. The method also returns the current instance,self. In.__exit__(), you decrease.level, so the printed text steps back one indentation level each time you exit the context.

The key point here is that returningself from.__enter__() allows you to reuse the same context manager across several nestedwith statements. This changes the text indentation level every time you enter and exit a given context.

A good exercise for you at this point would be to write a function-based version of this context manager. Function-based context managers is what you’ll be learning about next.

Creating Function-Based Context Managers

Python’scontextlib.contextmanager decorator provides an alternative and convenient way to implement the context management protocol. If you decorate an appropriately codedgenerator function with@contextmanager, then you get afunction-based context manager that automatically provides both.__enter__() and.__exit__().

The pattern to create a context manager using@contextmanager along with a generator function goes like this:

Python
>>>fromcontextlibimportcontextmanager>>>@contextmanager...defhello_context_manager():...print("Entering the context...")...yield"Hello, World!"...print("Leaving the context...")...>>>withhello_context_manager()ashello:...print(hello)...Entering the context...Hello, World!Leaving the context...

In this example, you can identify two visible sections inhello_context_manager(). Before theyield statement, you have the setup section. There, you can place the code that acquires the needed resources. Everything before theyield runs when the flow of execution enters the context.

After theyield statement, you have the teardown section where you can release resources and do the cleanup. The code afteryield runs at the end of thewith block. Theyield statement itself optionally provides the object that will be assigned to thewith target variable, if any.

This implementation and the one that uses the context management protocol are equivalent. Depending on which one you find suitable for your current problem, you might choose one over the other. A downside of the function-based implementation is that it requires an understanding of advanced Python topics, such asdecorators andgenerators.

The@contextmanager decorator reduces the boilerplate required to create a context manager. Instead of writing a whole class with.__enter__() and.__exit__() methods, you just need to implement a generator function with a singleyield that produces whatever you want.__enter__() to return.

For a more realistic example, you can use the@contextmanager to reimplement yourWritableFile context manager. Here’s what rewriting it with this technique will look like:

Python
>>>fromcontextlibimportcontextmanager>>>@contextmanager...defwritable_file(file_path):...try:...file=open(file_path,mode="w")...yieldfile...finally:...file.close()...>>>withwritable_file("hello.txt")asfile:...file.write("Hello, World!")...13

In this case,writable_file() is a generator function that opens a file for writing. Then, it temporarily suspends its execution andyields the file so thatwith can bind it to its target variable. When the execution flow leaves thewith code block, the function continues to execute and closes the file correctly.

Creating an Asynchronous Context Manager

To create an asynchronous context manager, you need to define the.__aenter__() and.__aexit__() methods. The script below is a reimplementation of the example about checking whether awebsite is online:

Pythonsite_checker_v2.py
importasyncioimportaiohttpclassAsyncSession:def__init__(self,url):self._url=urlasyncdef__aenter__(self):self.session=aiohttp.ClientSession()response=awaitself.session.get(self._url)returnresponseasyncdef__aexit__(self,exc_type,exc_value,exc_tb):awaitself.session.close()asyncdefcheck(url):asyncwithAsyncSession(url)asresponse:print(f"{url}: status ->{response.status}")html=awaitresponse.text()print(f"{url}: type ->{html[:17].strip()}")asyncdefmain():awaitasyncio.gather(check("https://realpython.com"),check("https://pycoders.com"),)asyncio.run(main())

This script works like the previous implementation. The main difference is that, in this example, you extract the logic of the original outerasync with statement and encapsulate it inAsyncSession.

In.__aenter__(), you create anaiohttp.ClientSession(), await the.get() response, and finally return the response itself. In.__aexit__(), you close the session, which corresponds to the teardown logic in this specific case. Note that.__aenter__() and.__aexit__() must return awaitable objects, so you need to define them using theasync def keywords.

If you run the script from your command line, then you get an output similar to this:

Shell
$pythonsite_checker_v2.pyhttps://realpython.com: status -> 200https://pycoders.com: status -> 200https://realpython.com: type -> <!doctype html>https://pycoders.com: type -> <!doctype html>

Great! Your script works just like its first version. It sendsGET requests to both sites concurrently and processes the corresponding responses.

Finally, a common practice when you’re writing asynchronous context managers is to implement the four special methods:

  1. .__aenter__()
  2. .__aexit__()
  3. .__enter__()
  4. .__exit__()

Doing so makes your context manager usable with bothwith andasync with statements.

Conclusion

You’ve learned about Python’swith statement, which helps you manage external resources safely. You’ve also learned how context managers can automatically handle thesetup andteardown phases of working with external resources, and how these tools can prevent resource leaks and improve code safety.

Additionally, you learned how to create custom context managers using both class-based and function-based approaches, and even how to implement asynchronous context managers for concurrent programming.

Thewith statement is an essential tool for Python developers. It facilitates resource management and ensures that resources are properly released, even in the presence of exceptions.

In this tutorial, you’ve learned how to:

  • Use Python’swith statement for resource management
  • Understand thecontext manager protocol
  • Create customclass-based andfunction-based context managers
  • Work withasynchronous context managers for concurrent tasks

With these skills, you can write more reliable and safer Python code by managing resources correctly and avoiding potential leaks. This knowledge strengthens your ability to build robust, maintainable applications.

Get Your Code:Click here to download the free sample code that shows you how to use Python’s with statement to manage external resources safely.

Frequently Asked Questions

Now that you have some experience with context managers and thewith statement in Python, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click theShow/Hide toggle beside each question to reveal the answer.

You use Python’swith statement to manage resource setup and teardown automatically, ensuring resources are properly released after use, even if exceptions occur.

Thewith statement reduces code complexity, enhances readability, and—more importantly—prevents resource leaks by ensuring resources are released correctly after use.

A context manager is a Python object that implements the.__enter__() and.__exit__() special methods to safely manage resources by automating setup and teardown steps.

Take the Quiz: Test your knowledge with our interactive “Context Managers and Python's with Statement” quiz. You’ll receive a score upon completion to help you track your learning progress:


Context Managers and Python's with Statement

Interactive Quiz

Context Managers and Python's with Statement

Test your knowledge of Python's with statement and context managers to write cleaner code and manage resources safely and efficiently.

Recommended Course

Context Managers and Using Python's with Statement(34m)

🐍 Python Tricks 💌

Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

AboutLeodanis Pozo Ramos

Leodanis is a self-taught Python developer, educator, and technical writer with over 10 years of experience.

» More about Leodanis

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

MasterReal-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

MasterReal-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.


Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics:intermediatepython

Related Learning Paths:

Related Courses:

Related Tutorials:

Keep reading Real Python by creating a free account or signing in:

Already have an account?Sign-In

Almost there! Complete this form and click the button below to gain instant access:

Python's with Statement: Manage External Resources Safely

Python's with Statement: Manage External Resources Safely (Sample Code)

🔒 No spam. We take your privacy seriously.


[8]ページ先頭

©2009-2026 Movatter.jp