Python String endswith: Syntax and Usage
python string endswith: Learn how to use Python's endswith() method to check string suffixes, handle multiple suffixes, limit search ranges, and avoid common pitfalls.
The python string 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, you must normalize both sides manually, typically by calling .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. 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 evaluated as a single argument. This is more readable and less error-prone than a chain of or expressions. It also avoids repeated calls to the method, which can matter in tight loops.
You can also 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 both 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: if start is negative, it counts from the end of the string, just like slice indices. The same applies to end. 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 is consistent with the mathematical 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:
# Wrong: suffix is not lowercased if name.lower().endswith(".PDF"): pass # Correct: both sides should be normalized if name.lower().endswith(".pdf".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 and is highly optimized. For a single suffix, it performs a direct comparison of the tail of the string. For a tuple, it iterates through the tuple and checks each suffix, but it 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 faster and more readable 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. However, if the set becomes large (dozens of entries), consider whether a regular expression or a set-based check might 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 digit or a specific format, re.search() with a pattern like r'\.\d{4}$' gives 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.