
Well, this might be kind of cheating but I did find it to be a useful tool for an assignment I got in my Python class. I had to check that a string matches a certain set of criterias (e.g. has lowercase letters, uppercase letters and so on [and yes, it IS a password checker]).
map() Approach — When You Need to Know Which Patterns MatchedIn order to achieve this I have imported the re module and have precompiled the individual regular expressions so that my code is a bit more modular. I have also used string.punctuation to make a regular expression checking for special characters.
import re
import string
lowercase_regex = re.compile("[a-z]")
uppercase_regex = re.compile("[A-Z]")
special_regex = re.compile('[' + re.escape(string.punctuation) + ']')
numbers_regex = re.compile("[0-9]")
This is nice and all but how am I going to compare all of those regular expressions against a single string? I could do a for loop but that seems like unnecessary complication. I have decided to use the map() function along with a re.findall() lambda and match the string against all regexes one after the other.
By doing that, we get a list of lists (after converting the map object, of course) containing the matches to each one of the regular expressions, and we can easily know which regular expression did not match (by checking if the respective index in the list is an empty list) and act accordingly.
Let’s see it in action. First, I’ll put the relevant regular expressions I want to check the string against in a list so that map() can iterate through it, and the I’ll call I’ll do some magic and eventually check the list.
>>> password = "Hello"
>>> password_regex = [lowercase_regex, uppercase_regex, numbers_regex, special_regex]
>>> password_regex_matches_list = list(map(lambda regex: re.findall(regex, password), password_regex))
>>> password_regex_matches_list
[['e', 'l', 'l', 'o'], ['H'], [], []]
Here you can see that the string “Hello” returned 4 matches for the 1st regex, which checked for lowercase letters, then 1 match for the uppercase regex and no matches for the other 2 (numbers and special characters).
Bonus: If you’d like to know how many matches there are in total, you can do this easily by iterating through to list of list with itertools.chain simply import it and run through the list.
>>> from itertools import chain
>>> password_regex_matches = list(chain.from_iterable(password_regex_matches_list))
>>> password_regex_matches
['e', 'l', 'l', 'o', 'H']
And that’s it, you’ve unpacked the list and can access all matches!
any() / all() Approach — When You Just Need a Yes or NoThe map() approach is great when you need the full picture — like showing the user exactly which password requirements they’re missing. But sometimes you just want a boolean: does this string match all (or any) of my patterns?
For that, a generator expression with all() or any() is cleaner:
>>> patterns = [lowercase_regex, uppercase_regex, numbers_regex, special_regex]
# True only if ALL patterns match
>>> all(re.search(p, password) for p in patterns)
False # "Hello" has no numbers or special characters
# True if ANY pattern matches
>>> any(re.search(p, password) for p in patterns)
True # "Hello" matches lowercase and uppercase
Short, readable, and no import of itertools. The tradeoff is you lose the per-pattern detail — you know the answer is “no” but not why. Pick your approach based on what you actually need:
map() — you get the full match list per pattern.all() / any() — simpler and more idiomatic.re.search vs re.match vs re.findallSince these come up a lot when working with multiple patterns, here’s the quick version:
re.match(pattern, string) — only checks at the beginning of the string. re.match("[0-9]", "abc123") returns None even though there are digits, because they’re not at the start.re.search(pattern, string) — scans the whole string and returns the first match it finds. Usually what you want.re.findall(pattern, string) — returns a list of all matches (which is why I used it above — I wanted to collect every character that matched each regex, not just the first one).For most multi-pattern checks, re.search is your friend. It’s the most forgiving of the three and behaves the way you’d intuitively expect.
If you liked this post, you can find all of my other content right here