AI Hints
Home›Python Tutorial›Generators, Decorators and Context Managers
Python · Lesson 24 of 49

Context Managers and Regular Expressions

Safe resources, and powerful pattern matching.

⏱ 2 min read▶ Video at 1:15:06🎓 Free
Watch this lesson in the Python courseWatch on YouTube ↗Full Python course

You have used with open, many times, and it always cleaned up after itself. Ava wants the same safety for her own resources, like a timer, or a database. And she also needs to find phone numbers and emails buried in customer messages. For the first, we use context managers. For the second, regular expressions.

Context Managers and Regular Expressions
From the video: Context Managers and Regular Expressions
  • with: set up, and always clean up
  • Build your own context manager
  • Search text by pattern

Writing a context manager

A context manager has two special methods. Enter runs when the with block begins, and exit runs when it ends, even if an error occurs. Here we print messages to see the order. This guarantees cleanup, such as closing a file, or releasing a lock.

ctx1.pypython
class Shop:
    def __enter__(self):
        print("Shop opens")
        return self
    def __exit__(self, exc_type, exc, tb):
        print("Shop closes")

with Shop() as s:
    print("Serving customers")
print("Done")
Output
Shop opens
Serving customers
Shop closes
Done

contextlib: the easy way

The contextlib module makes it shorter. Decorate a generator with context manager. The code before yield is the setup, and the code after it is the cleanup. Using try and finally guarantees the cleanup happens, even after an error. Here, a timer block reports when it finishes.

ctx2.pypython
from contextlib import contextmanager

@contextmanager
def opened(name):
    print("open", name)
    try:
        yield name
    finally:
        print("close", name)

with opened("orders") as n:
    print("working with", n)
Output
open orders
working with orders
close orders

Regular expressions

A regular expression is a pattern for text. The re module searches with it. Backslash d means a digit, and plus means one or more. Search finds the first match. Findall returns all of them. Sub replaces matches. And brackets capture parts. Use raw strings, with an r before the quote, so backslashes are left alone.

import re
msg = "Call 555-1234 or 555-9876, mail ava@leafy.com"
re.search(r"\d+-\d+", msg).group()
'555-1234'
re.findall(r"\d{3}-\d{4}", msg)
['555-1234', '555-9876']
re.findall(r"[\w.]+@[\w.]+", msg)
['ava@leafy.com']
re.sub(r"\d", "#", "id 42")
'id ##'

Regex cheat sheet

A few pieces cover most needs. Dot matches any character. Backslash d is a digit, w a word character, s a space. Star means zero or more, plus means one or more, question mark means optional. Curly brackets set exact counts. Square brackets list allowed characters. Caret and dollar anchor the start and end. Build patterns slowly, and test as you go.

PatternMeaningExample match
\da digit7
\w+a wordplant
[abc]one of a, b, cb
a{2}exactly two aaa
^ and $start and endwhole line
(…)capture a groupthe part you need

Parsing with groups

Groups pull structured data out of text. Here, Ava's order messages follow a pattern, a quantity, and a plant name. The pattern captures both. Match returns an object, and group gives each captured part. Named groups make the code self explaining. Regex is powerful, but keep patterns short and well tested.

regex_groups.pypython
import re

msg = "order: 3 x monstera"
m = re.match(r"order: (\d+) x (\w+)", msg)
print(m.group(1), m.group(2))
m2 = re.search(r"(?P<qty>\d+) x (?P<name>\w+)", msg)
print(m2.groupdict())
Output
3 monstera
{'qty': '3', 'name': 'monstera'}

Find the emails

Your turn. Use findall with a regular expression, to extract the email addresses from the text, and print them. Ready? Here is one solution. A pattern with word characters on both sides of an at sign. Real email patterns are longer, but this one works for simple cases.

✍️ Exercise: Find the emails

Use re.findall to extract all emails from the message and print them.

Show hint

pattern: [\w.]+@[\w.]+

Show solution
emails.pypython
import re

text = "Write to ava@leafy.com or sam@shop.org today"
print(re.findall(r"[\w.]+@[\w.]+", text))
Output
['ava@leafy.com', 'sam@shop.org']

Summary

Great work. You can build context managers with enter and exit, or with contextlib, and you can find and extract text with regular expressions. That completes the Pythonic toolbox. Next, we move towards professional habits, starting with type hints, and checking your code.

Key takeaways
  • with = setup + guaranteed cleanup
  • @contextmanager makes it short
  • re: search, findall, sub, groups
Next up: Type Hints — say what your code expects