19 min read

12 Ways to Check if a Python String Contains a Substring

Article Summary

Checking if a Python string contains a substring is a common coding task with several built-in solutions. This article covers in, __contains__(), find(), index(), count(), regex, and more.

Key takeaways

  • Use the in operator for a simple True or False check, and not in to confirm that a substring is missing.
  • Use find() when you need the position of a substring without raising an error if it isn’t found. Use index() when a missing substring should raise an error.
  • Use count() to tally non-overlapping occurrences, and startswith() or endswith() to check the beginning or end of a string.
  • Use casefold() or lower() on both strings for a case-insensitive check.
  • Use regular expressions for pattern matching or pandas str.contains() to search an entire DataFrame column.

Checking whether a string contains a substring is a common task in Python. Whether you’re searching user input, processing text files, or working with data, you’ll often need to know if a particular word, character, or sequence of characters appears inside a larger string.

Fortunately, Python gives you several ways to check if a string contains a substring. The simplest is the in operator. For example, ‘string’ in my_string returns True if the substring exists and False if it doesn’t.

Python remains one of the most widely used programming languages. According to Stack Overflow’s 2026 Developer Survey, 58% of respondents reported doing extensive development work in Python during the previous year.1 That makes understanding everyday string operations a valuable skill for developers.

Although the in operator is usually the best choice when you only need to check whether a substring exists, other Python methods can give you additional information. You might want to find the position of a substring, count how many times it appears, or match a specific pattern.

In this guide, we’ll explore the different ways to check if a Python string contains a substring, with examples to help you choose the right approach.

How the Python substring methods compare

Before exploring each method, here’s a quick comparison of what the different approaches return and when you might use them.

MethodReturnsIf not foundBest for
inTrue or FalseFalseSimple substring checks
__contains__()True or FalseFalseUnderstanding how in works
find()Index of the first match-1Finding a position without an exception
index()Index of the first matchRaises ValueErrorFinding a position expected to exist
count()Number of matches0Counting non-overlapping occurrences
startswith() / endswith()True or FalseFalseChecking the beginning or end
rfind() / rindex()Index of the last match-1 / ValueErrorFinding the last occurrence
re.search()Match object or NoneNoneMatching regular-expression patterns
operator.contains()True or FalseFalseUsing containment as a function
pandas str.contains()Boolean Series, possibly with missing valuesFalse for nonmatching text; missing values depend on dtype and naChecking an entire DataFrame column

Most of these methods are built into Python and don’t require installing any additional packages. The main exception is pandas, which is a third-party library used for data analysis.

For everyday substring checks, start with in. The other methods are useful when you have more specific requirements.

Using Python’s __contains__() special method

The Python str class has a special method called __contains__() that checks whether a string contains a particular substring.

This method returns True when the substring is found and False when it isn’t.

You may notice the double underscores at the beginning and end of the method name. These identify it as a special method, sometimes called a dunder method.

Python uses special methods to implement operations such as comparisons, arithmetic, and membership tests. When you use the in operator with a string, Python invokes its containment behavior through __contains__().

The double underscores don’t prevent a subclass from overriding this method. Python’s name-mangling rules apply to identifiers that begin with at least two underscores but don’t end with two underscores.

You can call __contains__() directly on a string:

my_string = 'This is the string being checked.'

my_substring = 'string'

if my_string.__contains__(my_substring):

    print('String contains substring!')

else:

    print('String does not contain substring')

Output:

String contains substring!

You can also call the method directly through Python’s str class:

my_string = 'This is the string being checked.'

my_substring = 'string'

if str.__contains__(my_string, my_substring):

    print('String contains substring!')

else:

    print('String does not contain substring')

This produces the same output.

When calling str.__contains__() directly, the first argument is the string you’re searching, and the second is the substring you’re looking for.

Both approaches work, but calling __contains__() directly is uncommon in everyday Python code. The in operator is shorter, more readable, and generally preferred for checking whether a substring exists.

Understanding __contains__() can still be useful when you’re learning how Python’s special methods work.

If you’re ready to take your Python skills further, explore the wide range of Python courses on Udemy to practice these concepts and build your programming confidence.

Using the Python string index() method

Sometimes, simply knowing that a substring exists isn’t enough. You may also need to know where it appears.

Python’s index() method returns the starting index of the first occurrence of a substring.

For example, imagine you’re working with a greeting template that contains a placeholder for a person’s name.

template = 'Hello {name}, How are you!'

name_index = template.index('{name}')

print(name_index)  # 6

The output is 6 because Python uses zero-based indexing, meaning the first character in a string has an index of 0.

The substring {name} begins at index 6.

This information could be useful if you need to extract part of the string or identify where a particular piece of text appears.

However, there’s one important limitation.

If index() doesn’t find the substring, it raises a ValueError.

For example:

template = 'Hello {name}, How are you!'

name_index = template.index('{age}')

Because {age} doesn’t appear in the template, Python raises a ValueError with a message indicating that the substring wasn’t found.

This behavior can be useful when the substring is expected to exist and its absence indicates a problem.

If the substring might legitimately be missing, you may prefer the find() method.

Using the Python string find() method

The find() method works similarly to index(), but it handles missing substrings differently.

Instead of raising a ValueError, find() returns -1 when the substring isn’t found.

When it finds a match, it returns the starting index of the first occurrence.

Here’s an example:

template = 'Hello {name}, How are you!'

name_index = template.find('{name}')

print(name_index)  # 6

If you search for a substring that doesn’t exist, the result is -1:

template = 'Hello {name}, How are you!'

name_index = template.find('{age}')

print(name_index)  # -1

This makes find() useful when you need to locate a substring but don’t want a missing match to raise an exception.

Finding a substring within a specific range

The find() method also accepts optional start and end arguments.

Its syntax is:

string.find(substring, start, end)

These arguments let you search within a specific part of a string.

  • substring is the text you’re looking for.
  • start is the index where the search begins.
  • end is the index where the search stops, excluding the character at that index.

Here’s an example:

my_string = 'She sells sea shells and sea glass by the sea shore'

my_substring = 'sea'

print(my_string.find(my_substring, 11, 40))  # 25

The string contains three occurrences of sea, starting at indices 10, 25, and 42.

Because the search starts at index 11 and ends before index 40, the method skips the first occurrence and returns 25.

This is helpful when you need to search a particular section of a string or locate additional occurrences after an earlier match.

Finding the last occurrence with rfind() and rindex()

Python also provides rfind() and rindex().

These methods are similar to find() and index(), except they return the starting position of the last occurrence rather than the first.

For example:

my_string = 'cat dog cat'

print(my_string.find('cat'))   # 0

print(my_string.rfind('cat'))  # 8

Like find(), rfind() returns -1 when there is no match.

The rindex() method raises a ValueError when the substring isn’t found, just like index().

Choose between these methods based on whether you need the first or last occurrence and how you want to handle missing text.

Using the Python string startswith() and endswith() methods

Sometimes, you don’t need to search an entire string. You only want to know whether it begins or ends with a particular substring.

Python provides two methods for this:

  • startswith() checks the beginning of a string.
  • endswith() checks the end of a string.

Both methods return True or False.

For example:

file_name = 'report_2026.csv'

print(file_name.startswith('report'))  # True

print(file_name.endswith('.csv'))      # True

These methods are especially useful when working with filenames, URLs, prefixes, or text with a predictable structure.

Checking multiple options at once

Both methods also accept a tuple of strings.

For example, you might want to check whether a filename ends with either .csv or .xlsx.

file_name = 'report_2026.csv'

print(file_name.endswith(('.csv', '.xlsx')))  # True

The result is True because the filename ends with one of the supplied suffixes.

Similarly, you can check several possible prefixes:

file_name = 'report_2026.csv'

print(file_name.startswith(('report', 'summary')))  # True

Both methods also accept optional start and end arguments, allowing you to check the boundaries of a selected portion of the string.

Keep in mind that these methods only check the beginning or end of the specified string or range. They won’t find a substring that appears exclusively in the middle.

Using the Python string count() method

If you want to know how many times a substring appears, Python’s count() method is a useful choice.

Instead of returning a Boolean or an index, it returns the number of non-overlapping occurrences of a substring.

Here’s an example:

my_string = 'She sells sea shells and sea glass by the sea shore'

my_substring = 'sea'

print(my_string.count(my_substring))  # 3

The result is 3 because sea appears three times.

If the substring doesn’t appear at all, count() returns 0.

You can also use the result to check whether a substring exists:

my_string = 'She sells sea shells'

if my_string.count('sea') > 0:

    print('Substring found!')

However, if you only need to know whether a substring exists, the in operator is clearer.

How count() handles overlapping matches

One important detail is that count() only counts non-overlapping occurrences.

For example:

my_string = 'aaaa'

print(my_string.count('aa'))  # 2

Although aa could begin at three different positions, count() returns 2 because it counts non-overlapping matches.

Another edge case involves empty strings:

print('abc'.count(''))  # 4

Python counts the empty substring at each boundary between characters, including the beginning and end of the string.

For a string of length three, that produces four empty-substring occurrences.

Using the Python in operator

For most everyday substring checks, the in operator is the simplest and most readable approach.

It checks whether one string appears inside another and returns True or False.

For strings, the expression:

substring in my_string

evaluates to True when the substring exists anywhere inside my_string.

Here’s an example:

my_string = 'This is the string being checked.'

my_substring = 'string'

if my_substring in my_string:

    print('String contains substring!')

else:

    print('String does not contain substring')

Output:

String contains substring!

The in operator uses Python’s containment behavior, which strings implement through __contains__().

You can also use in to check membership in other Python collections, including lists, tuples, dictionaries, and sets. However, the exact meaning depends on the collection type.

Checking whether a substring is missing

If you want to confirm that a substring doesn’t exist, use the not in operator.

my_string = 'She sells sea shells'

print('sand' not in my_string)  # True

print('sea' not in my_string)   # False

The expression returns True when the substring is absent.

What happens when the substring is empty?

One behavior that sometimes surprises beginners is that an empty string counts as a substring of every string.

For example:

print('' in 'abc')  # True

print('' in '')     # True

This is expected Python behavior, not an error.

If you’re checking text provided by a user, you may want to verify that the search term isn’t empty before performing the containment check.

my_string = 'Hello, Python!'

my_substring = ''

if my_substring and my_substring in my_string:

    print('Substring found!')

else:

    print('No non-empty substring found.')

This prevents an empty search term from being treated as a meaningful match.

Using regular expressions

Python supports regular expressions through its built-in re module.

Regular expressions let you search for patterns rather than only exact sequences of characters.

For example, you might want to find a number, an email-like pattern, or a word that follows a particular format.

The re.search() function searches a string for a pattern and returns a match object when it finds one. If no match exists, it returns None.

Here’s a simple example:

import re

my_string = 'This is the string being checked.'

my_substring = 'string'

if re.search(my_substring, my_string):

    print('String contains substring!')

else:

    print('String does not contain substring')

Output:

String contains substring!

This works similarly to in for the example above, but regular expressions offer much more flexibility.

For example, suppose you want to check whether a string contains a sequence of digits.

import re

my_string = 'Order 12345 has shipped'

match = re.search(r'\d+', my_string)

if match:

    print(match.group())  # 12345

In this example, \d+ matches one or more Unicode decimal digits.

When should you use regular expressions?

Regular expressions are useful when the text you’re looking for follows a pattern rather than an exact value.

However, re.search() is usually unnecessary when you only want to check whether a literal substring exists.

There’s also an important distinction: re.search() interprets its first argument as a regular-expression pattern, not necessarily as plain text.

Characters such as ., *, and + have special meanings in regular expressions.

If you need to match user-provided text literally while still using regular expressions, you can escape the special characters with re.escape().

import re

my_string = 'The file is report.csv'

my_substring = 'report.csv'

match = re.search(re.escape(my_substring), my_string)

print(bool(match))  # True

For ordinary literal substring checks, though, in remains the simpler option.

Using pandas to check if a string contains a substring

If you’re working with a pandas DataFrame, you may need to check an entire column of strings rather than one string at a time.

The pandas library provides Series.str.contains() for this purpose.

It tests each value in a Series and returns a Boolean Series indicating which values match your search.

You can use the result to filter rows in a DataFrame or analyze text data.

Here’s an example:

import pandas as pd

shells = pd.Series(['Sea shell', 'sea glass', 'Sand dollar', None])

result = shells.str.contains(

    'sea',

    case=False,

    na=False,

    regex=False

)

print(result)

Output:

0     True

1     True

2    False

3    False

dtype: bool

The result shows that the first two values contain sea, while the third doesn’t. The missing value is also treated as False because we specified na=False.

Understanding the str.contains() parameters

Three parameters are especially useful:

  • case=False makes the search case-insensitive. This allows sea to match both Sea and sea.
  • na=False treats missing values, such as None or NaN, as False. This can make the result easier to use as a Boolean filter.
  • regex=False treats the search term as literal text instead of a regular-expression pattern.

By default, pandas str.contains() uses regex=True, meaning your search term is interpreted as a regular expression.

The default handling of missing values also depends on the Series data type. For example, object-dtype and nullable-string Series may preserve missing values in different ways, while the newer pandas string dtype uses False by default.

Setting na=False explicitly makes your intended behavior clear.

It’s also important to distinguish a missing value from an empty string. An empty string (”) is a valid string value, not missing data.

Filtering a DataFrame with str.contains()

You can use the same method to filter rows in a DataFrame.

import pandas as pd

df = pd.DataFrame({

    'product': ['Sea shell', 'Sea glass', 'Sand dollar', None],

    'price': [5, 12, 8, 0]

})

matches = df['product'].str.contains(

    'sea',

    case=False,

    na=False,

    regex=False

)

print(df[matches])

This selects the rows containing sea, regardless of capitalization.

Using str.contains() is especially useful when analyzing datasets with many rows.

For a single ordinary Python string, however, the in operator is usually more straightforward.

Using the operator.contains() function

Python also provides a contains() function through its built-in operator module.

This function performs the same type of membership test as the in operator.

To use it, import the operator module:

import operator

my_string = 'This is the string being checked.'

my_substring = 'string'

if operator.contains(my_string, my_substring):

    print('String contains substring!')

else:

    print('String does not contain substring')

Output:

String contains substring!

Notice the argument order:

operator.contains(my_string, my_substring)

The string being searched comes first, followed by the substring.

That’s the opposite of how the equivalent in expression is written:

my_substring in my_string

Both expressions return the same Boolean result.

You generally won’t need operator.contains() for simple string checks. Its main advantage is that it gives you containment testing as a function, which can be useful in code that works with callable functions.

For most developers, the in operator is easier to read.

Things to remember when checking if a Python string contains a substring

Python offers several methods for searching strings, but they aren’t completely interchangeable.

Before choosing a method, consider what you’re trying to accomplish:

  • Do you only need a Boolean result? Use in or not in.
  • Do you need the first position of a substring? Use find() or index().
  • Do you need the last position? Use rfind() or rindex().
  • Do you want to count occurrences? Use count().
  • Are you checking the beginning or end of a string? Use startswith() or endswith().
  • Do you need to match a pattern? Consider the re module.
  • Are you checking an entire pandas column? Use Series.str.contains().
  • Do you need a case-insensitive search? Use an appropriate case-normalization method or a case-insensitive matching option.

Python’s standard string containment and search methods are case-sensitive. That means uppercase and lowercase letters are treated as different characters.

Regular expressions and pandas provide options for changing that behavior.

Check if a string contains a substring in Python, ignoring case

One of the easiest ways to perform a case-insensitive substring search is to convert both strings to a consistent case before comparing them.

You can do this with lower() or upper().

For example:

my_string = 'She sells SEA shells'

my_substring = 'Sea'

print(my_substring.lower() in my_string.lower())  # True

Both strings are converted to lowercase before the containment check.

For many everyday applications, this is sufficient.

However, Python also provides the casefold() method, which is designed for more comprehensive case-insensitive comparisons.

my_string = 'She sells SEA shells'

my_substring = 'Sea'

print(my_substring.casefold() in my_string.casefold())  # True

The difference becomes more important when working with international text.

For example, the German letter ß is converted to ss by casefold().

word1 = 'STRASSE'

word2 = 'straße'

print(word1.lower() == word2.lower())      # False

print(word1.casefold() == word2.casefold())  # True

The lower() method doesn’t handle this particular comparison because it leaves ß unchanged.

By contrast, casefold() removes more case distinctions, allowing the two words to match.

For general-purpose case-insensitive substring checks, casefold() is often a good choice, especially when your text may contain characters from different languages.

Keep in mind that case folding isn’t the same as Unicode normalization. Two strings can appear identical but use different underlying Unicode character sequences.

For applications that need to handle those differences, Python’s unicodedata.normalize() function may also be useful.

Learn Python with Udemy

Knowing how to check whether a Python string contains a substring is just one of many useful programming skills.

As you become more comfortable with Python, you’ll find yourself using string methods for everything from validating user input to processing data, automating tasks, and building applications.

The best way to build confidence is to practice these techniques in real projects.

Udemy offers Python courses for beginners and experienced developers alike. Whether you’re learning your first programming language or expanding your existing skills, hands-on exercises can help you turn these concepts into practical knowledge.

FAQ

Does == work to check for a substring?

No. The == operator checks whether two strings are equal, not whether one string appears inside another. For example:

print('sea' == 'sea shore')  # False

print('sea' in 'sea shore')  # True

Use == when you want to compare two complete strings for equality. Use in when you want to check whether one string appears anywhere inside another.

How do I check if a string contains a number?

You can use Python’s any() function with the isdigit() method to check whether a string contains at least one digit.

my_string = 'Order 66 shipped'

print(any(char.isdigit() for char in my_string))  # True

This works because isdigit() returns True for Unicode digit characters. If you specifically want to detect only ASCII digits from 0 through 9, use:

my_string = 'Order 66 shipped'

print(any('0' <= char <= '9' for char in my_string))  # True

Both approaches are useful, but they differ in how they handle certain non-ASCII characters.

How do I check if a string contains a specific character?

Use the in operator with a one-character string. For example:

email = '[email protected]'

print('@' in email)  # True

A single character is simply a substring of length one, so you can use the same string-searching methods discussed in this article.

How do I check if a string contains any of several substrings?

Use Python’s any() function with the in operator.

my_string = 'She sells sea glass'

substrings = ('sand', 'glass')

print(any(word in my_string for word in substrings))  # True

The result is True because at least one of the substrings appears in my_string. If you need every substring to be present, use all() instead:

my_string = 'She sells sea glass'

substrings = ('sea', 'glass')

print(all(word in my_string for word in substrings))  # True

How do I find all occurrences of a substring in Python?

If you only need the number of non-overlapping occurrences, use count(). If you need their positions, you can repeatedly call find(). For example:

my_string = 'cat dog cat'

substring = 'cat'

positions = []

start = 0

while True:

    position = my_string.find(substring, start)

    if position == -1:

        break

    positions.append(position)

    start = position + len(substring)

print(positions)  # [0, 8]

This example collects the starting positions of all non-overlapping matches. If you need overlapping matches, the search logic must advance differently. An empty search term also requires special handling.

What’s the best way to check if a Python string contains a substring?

For most situations, use the in operator. It’s simple, readable, and returns a Boolean result without requiring additional imports.

my_string = 'Learning Python is fun'

print('Python' in my_string)  # True

Choose a different method only when you need information beyond whether the substring exists, such as its position, the number of occurrences, or pattern-matching behavior.

  1. Stack Exchange Inc. (2026), “Programming, scripting, and markup languages,” Stack Overflow Developer Survey 2026: https://survey.stackoverflow.co/2026/technology/data/languages ↩︎