In this guide, we'll take a look at how to check if a string contains a substring in Python. As usual, each approach we'll cover has different pros and cons.
The in Operator
The easiest way to check if a Python string contains a substring is to use the in
operator.
The in
operator is used to check data structures for membership in Python. It returns a Boolean [either True
or False
]. To check if a string contains
a substring in Python using the in
operator, we simply invoke it on the superstring:
fullstring = "StackAbuse"
substring = "tack"
if substring in fullstring:
print["Found!"]
else:
print["Not found!"]
This operator is shorthand for calling an object's __contains__
method, and also works well for checking if an item exists in a list. It's worth noting that it's not null-safe, so if our fullstring
was pointing to None
, an exception would be thrown:
TypeError: argument of type 'NoneType' is not iterable
To avoid this, you'll first want to check whether it points to None
or not:
fullstring = None
substring = "tack"
if fullstring != None and substring in fullstring:
print["Found!"]
else:
print["Not found!"]
The String.index[] Method
The String type in Python has a method called index[]
that can be used to find the starting index of the first occurrence of a substring in a string.
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
If the substring is not found, a ValueError
exception is thrown, which can be
handled with a try-except-else block:
fullstring = "StackAbuse"
substring = "tack"
try:
fullstring.index[substring]
except ValueError:
print["Not found!"]
else:
print["Found!"]
This method is useful if you need to know the position of the substring, as opposed to just its existence within the full string.
The String.find[] Method
The String type has another method called find
which is more convenient to use than index[]
, because we don't need to worry about handling any exceptions.
If find[]
doesn't find a match, it returns -1, otherwise it returns the left-most index
of the substring in the larger string.
fullstring = "StackAbuse"
substring = "tack"
if fullstring.find[substring] != -1:
print["Found!"]
else:
print["Not found!"]
If you'd prefer to avoid the need to catch errors, then this method should be favored over index[]
.
Regular Expressions [RegEx]
Regular expressions provide a more flexible [albeit more complex] way to check strings for pattern matching. Python is shipped with a built-in module for regular expressions, called re
. The re
module contains a function called search[]
, which we can use to match a
substring pattern:
from re import search
fullstring = "StackAbuse"
substring = "tack"
if search[substring, fullstring]:
print "Found!"
else:
print "Not found!"
This method is best if you are needing a more complex matching function, like case insensitive matching. Otherwise the complication and slower speed of regex should be avoided for simple substring matching use-cases.
This article was written by Jacob Stopak, a software consultant and developer with passion for helping others improve their lives through code. Jacob is the creator of Initial Commit - a site dedicated to helping curious developers learn how their favorite programs are coded. Its featured project helps people learn Git at the code level.
Does Python have a string contains substring method?
99% of use cases will be covered using the keyword, in
, which returns True
or False
:
'substring' in any_string
For the use case of getting the index, use str.find
[which returns -1 on failure, and has optional positional arguments]:
start = 0
stop = len[any_string]
any_string.find['substring', start, stop]
or str.index
[like find
but raises ValueError on failure]:
start = 100
end = 1000
any_string.index['substring', start, end]
Explanation
Use the in
comparison
operator because
- the language intends its usage, and
- other Python programmers will expect you to use it.
>>> 'foo' in '**foo**'
True
The opposite [complement], which the original question asked for, is not in
:
>>> 'foo' not in '**foo**' # returns False
False
This is semantically the same as not 'foo' in '**foo**'
but it's much more readable and explicitly provided for in the language as a readability improvement.
Avoid using __contains__
The "contains" method implements the behavior for in
. This example,
str.__contains__['**foo**', 'foo']
returns True
. You could also call this function from the instance of the superstring:
'**foo**'.__contains__['foo']
But don't. Methods that start with underscores are considered semantically non-public. The only reason to use this is when implementing or extending the in
and not in
functionality [e.g. if subclassing str
]:
class NoisyString[str]:
def __contains__[self, other]:
print[f'testing if "{other}" in "{self}"']
return super[NoisyString, self].__contains__[other]
ns = NoisyString['a string with a substring inside']
and now:
>>> 'substring' in ns
testing if "substring" in "a string with a substring inside"
True
Don't use find
and index
to test for "contains"
Don't use the following string methods to test for "contains":
>>> '**foo**'.index['foo']
2
>>> '**foo**'.find['foo']
2
>>> '**oo**'.find['foo']
-1
>>> '**oo**'.index['foo']
Traceback [most recent call last]:
File "", line 1, in
'**oo**'.index['foo']
ValueError: substring not found
Other languages may have no methods to directly test for substrings, and so you would have to use these types of methods, but with Python, it is much more efficient to use the in
comparison operator.
Also, these are not drop-in replacements for in
. You may have to handle the exception or -1
cases, and if they return 0
[because they found the substring at the beginning] the boolean interpretation is False
instead of True
.
If you really mean not any_string.startswith[substring]
then say it.
Performance comparisons
We can compare various ways of accomplishing the same goal.
import timeit
def in_[s, other]:
return other in s
def contains[s, other]:
return s.__contains__[other]
def find[s, other]:
return s.find[other] != -1
def index[s, other]:
try:
s.index[other]
except ValueError:
return False
else:
return True
perf_dict = {
'in:True': min[timeit.repeat[lambda: in_['superstring', 'str']]],
'in:False': min[timeit.repeat[lambda: in_['superstring', 'not']]],
'__contains__:True': min[timeit.repeat[lambda: contains['superstring', 'str']]],
'__contains__:False': min[timeit.repeat[lambda: contains['superstring', 'not']]],
'find:True': min[timeit.repeat[lambda: find['superstring', 'str']]],
'find:False': min[timeit.repeat[lambda: find['superstring', 'not']]],
'index:True': min[timeit.repeat[lambda: index['superstring', 'str']]],
'index:False': min[timeit.repeat[lambda: index['superstring', 'not']]],
}
And now we see that using in
is much faster than the others. Less time to do an equivalent operation is better:
>>> perf_dict
{'in:True': 0.16450627865128808,
'in:False': 0.1609668098178645,
'__contains__:True': 0.24355481654697542,
'__contains__:False': 0.24382793854783813,
'find:True': 0.3067379407923454,
'find:False': 0.29860888058124146,
'index:True': 0.29647137792585454,
'index:False': 0.5502287584545229}
How can in
be faster than __contains__
if in
uses __contains__
?
This is a fine follow-on question.
Let's disassemble functions with the methods of interest:
>>> from dis import dis
>>> dis[lambda: 'a' in 'b']
1 0 LOAD_CONST 1 ['a']
2 LOAD_CONST 2 ['b']
4 COMPARE_OP 6 [in]
6 RETURN_VALUE
>>> dis[lambda: 'b'.__contains__['a']]
1 0 LOAD_CONST 1 ['b']
2 LOAD_METHOD 0 [__contains__]
4 LOAD_CONST 2 ['a']
6 CALL_METHOD 1
8 RETURN_VALUE
so we see that the .__contains__
method has to be separately
looked up and then called from the Python virtual machine - this should adequately explain the difference.