Python String endswith: Syntax and Usage
Learn how to use Python's str.endswith() method: syntax, tuple suffixes, start and end parameters, and common edge-case mistakes.
Python's str.endswith() method is a built-in way to test whether a string ends with a given suffix. It returns True if the string ends with the specified substring, and False otherwise. This is a common operation when validating file extensions, parsing URLs, or processing user input. The method is simple to use, but it has a few nuances that matter in real code.
What endswith() Does and Its Basic Syntax
The endswith() method is defined on Python string objects. Its signature is:
str.endswith(suffix[, start[, end]])
The suffix parameter can be a string or a tuple of strings. The optional start and end parameters define a slice of the original string to check, similar to how slicing works. If omitted, the check runs against the entire string.
Here is the most basic usage:
filename = 'report.pdf' print(filename.endswith('.pdf')) # True print(filename.endswith('.docx')) # False
The method returns a boolean, so it fits directly into conditional expressions. For example:
if filename.endswith('.pdf'): print('Opening PDF viewer')
Using endswith() with a Single Suffix
When you pass a single string as the suffix, Python checks whether the string ends with exactly that substring. The comparison is case-sensitive. This means 'Report.PDF'.endswith('.pdf') returns False. If case-insensitive behavior is required, normalize both sides to the same case, typically with .lower() or .casefold():
name = 'Report.PDF' if name.lower().endswith('.pdf'): print('PDF file')
This works because lower() creates a new string with all characters converted to lowercase, and then endswith() checks the suffix against that new string. If the suffix comes from a variable, normalize it too:
name = 'Report.PDF' suffix = '.PDF' if name.lower().endswith(suffix.lower()): print('PDF file')
Note that lower() is not always the same as casefold() for Unicode text, so choose based on your data.
Checking Against Multiple Suffixes with a Tuple
A common need is to check whether a string ends with any of several suffixes. Instead of writing multiple or conditions, you can pass a tuple of suffixes to endswith(). The method returns True if the string ends with any element of the tuple.
image_file = 'photo.jpg' if image_file.endswith(('.jpg', '.jpeg', '.png')): print('Image file')
The tuple is a single argument, so this is more readable and less error-prone than a chain of or expressions. It also avoids repeated method calls at the Python level.
You can combine a tuple with start and end parameters. The tuple applies to the same slice of the string.
Limiting the Search with start and end
The optional start and end parameters let you check a substring of the original string without creating a new slice. This is memory-efficient and convenient when you already know the relevant range.
url = 'https://example.com/download/file.pdf' # Check only the last part after the last slash start = url.rfind('/') + 1 print(url.endswith('.pdf', start)) # True
In this example, start is the index just after the final slash, so endswith() only examines the substring from that position to the end. The end parameter works similarly, giving an exclusive end index. The behavior matches Python slicing: s[start:end] is the substring that gets checked.
One subtle point: negative values for start or end count from the end of the string, just like slice indices. This can be useful for checking a fixed-length suffix without computing absolute positions.
Common Edge Cases and Mistakes
Several edge cases trip up experienced developers. Understanding them prevents subtle bugs.
Empty suffix: An empty string is always considered a suffix. 'hello'.endswith('') returns True. This follows the standard definition of a suffix, but it can surprise people who expect False.
Empty string: ''.endswith('.pdf') returns False because an empty string does not end with a non-empty suffix. However, ''.endswith('') returns True.
Case sensitivity: As noted earlier, the comparison is case-sensitive. For case-insensitive checks, normalize the string and the suffix together. A common mistake is to normalize only one side:
name = 'Report.PDF' suffix = '.PDF' # Wrong: only the string is lowercased, not the suffix if name.lower().endswith(suffix): pass # Correct: normalize both sides before comparing if name.lower().endswith(suffix.lower()): pass
Tuple with non-string elements: If you pass a tuple that contains a non-string, endswith() raises a TypeError. Ensure all tuple elements are strings.
Whitespace: endswith() does not strip whitespace. 'hello '.endswith('hello') returns False. If you need to ignore trailing spaces, call .rstrip() first.
Performance and Maintainability Considerations
endswith() is implemented in C in CPython and is fast for ordinary suffix checks. For a tuple, it checks each suffix in turn and short-circuits on the first match. This is usually faster than a Python-level loop with multiple or conditions.
When you need to check many strings against the same set of suffixes, building the tuple once and reusing it avoids repeated tuple construction. For example, in a loop that processes thousands of filenames, define the tuple outside the loop:
suffixes = ('.jpg', '.jpeg', '.png') for file in files: if file.endswith(suffixes): process(file)
This is both clearer and more efficient than recreating the tuple each iteration.
From a maintainability perspective, using a tuple of suffixes makes the allowed set explicit. If the set grows later, you only update one place. If the set becomes large, a regular expression may be clearer. For most practical cases, endswith() with a tuple is the right tool.
When to Reach for Alternatives
endswith() is the right choice for simple suffix checks. But there are situations where another approach is better.
Regular expressions: If you need to match a pattern rather than a fixed suffix, such as a filename ending with a dot followed by four digits, re.search() can give you more control. However, regular expressions are slower and more complex, so use them only when the pattern is genuinely dynamic.
Pathlib: For filesystem paths, pathlib.Path objects have a .suffix property that returns the file extension. This is often more semantic than calling endswith() on a string. For example:
from pathlib import Path p = Path('archive.tar.gz') print(p.suffix) # '.gz'
But Path.suffix only returns the last suffix, not a compound one like .tar.gz. If you need to check for compound suffixes, endswith() is simpler.
String slicing: You could manually compare the last characters using slicing, but endswith() is more readable and handles edge cases like empty strings correctly. There is no performance benefit to manual slicing in CPython.
In summary, endswith() is the standard, efficient way to check string suffixes. It handles multiple suffixes cleanly, supports slicing with start and end, and is easy to read. Keep the edge cases in mind, and you'll avoid the most common mistakes.