4.3 String Inspection and Classification Methods
Key Takeaways
- `str.find()` and `str.rfind()` return `-1` when a target substring is missing, whereas `str.index()` and `str.rindex()` raise a `ValueError`.
- `str.count(sub, start, end)` counts non-overlapping occurrences of a substring, and an empty substring argument `s.count('')` evaluates to `len(s) + 1`.
- `startswith()` and `endswith()` verify boundary substrings and accept a tuple of candidate prefixes or suffixes for multi-pattern evaluation.
- Character classification methods (`isalpha()`, `isdigit()`, `isdecimal()`, `isnumeric()`, `isalnum()`, `isspace()`, `islower()`, `isupper()`, `istitle()`) return boolean status flags based on Unicode properties.
- All string classification predicate methods return `False` when invoked on an empty string (`""`), as they strictly require at least one qualifying character.
String Inspection and Classification Methods
Python provides a comprehensive suite of built-in string methods designed to search, inspect, count, and classify textual data. Mastering their specific return types, boundary parameters, and exception handling is essential for the PCAP certification.
1. Substring Searching: find() vs. index()
Python offers four primary methods to locate the position of a substring within a string:
+-------------+----------------------+-----------------------+
| Direction | Returns -1 on Miss | Raises ValueError |
+-------------+----------------------+-----------------------+
| Left-to-Right | str.find(sub) | str.index(sub) |
| Right-to-Left | str.rfind(sub) | str.rindex(sub) |
+-------------+----------------------+-----------------------+
Method Signatures
All four methods share identical parameter structures:
startandendinterpret search bounds identically to slice syntax[start:end].- The index returned is always relative to the start of the entire original string, never relative to
start.
text = "banana"
# Left-to-right searching
print(text.find("an")) # 1 (first 'an' begins at index 1)
print(text.index("an")) # 1
# Right-to-left searching
print(text.rfind("an")) # 3 (last 'an' begins at index 3)
print(text.rindex("an")) # 3
# Searching with slice boundaries [2:5]
print(text.find("an", 2, 5)) # 3
The Critical Difference: Missing Substring Behavior
The single most tested distinction on the PCAP exam is how these methods handle missing substrings:
text = "developer"
# find() and rfind() return -1 when the substring is absent
print(text.find("xyz")) # -1
print(text.rfind("xyz")) # -1
# index() and rindex() raise ValueError when the substring is absent
try:
text.index("xyz")
except ValueError as err:
print(f"Caught: {err}") # Caught: substring not found
2. Counting Occurrences: count()
The count() method returns the number of non-overlapping occurrences of a substring within an optional slice range [start:end]:
The Non-Overlapping Rule
Matches are consumed from left to right. Characters inside an identified match cannot participate in subsequent matches:
# In 'aaaa', non-overlapping pairs are indices 0-1 and 2-3
print("aaaa".count("aa")) # 2 (not 3!)
# In 'banana', matches for 'ana' occur at index 1; remaining 'na' does not match
print("banana".count("ana")) # 1
Edge Cases with count()
- Empty Substring (
""): Returnslen(s) + 1(the number of zero-width positions before, between, and after characters):print("dog".count("")) # 4 (len("dog") + 1) print("".count("")) # 1 - Substring Not Found: Returns
0without raising any exceptions.
3. Prefix and Suffix Verification: startswith() and endswith()
The startswith() and endswith() methods test whether a string begins or ends with a specific sequence:
filename = "project_report_2026.pdf"
print(filename.startswith("project")) # True
print(filename.endswith(".pdf")) # True
print(filename.startswith("Report")) # False (case-sensitive)
Passing a Tuple of Candidates
Both methods accept a tuple of strings to check against multiple possibilities simultaneously:
url = "https://openedg.org"
# Check multiple prefixes with a tuple
print(url.startswith(("http://", "https://"))) # True
# Check multiple file extensions
image_file = "header_logo.png"
valid_extensions = (".jpg", ".jpeg", ".png", ".webp")
print(image_file.endswith(valid_extensions)) # True
Exam Trap: Passing a
listorsetinstead of atupleraises aTypeError:# Raises TypeError: tuple for startswith -- or str -- required, not list filename.endswith([".pdf", ".txt"])
4. Character Classification and Predicate Methods
Python provides a robust set of boolean predicate methods to classify the characters within a string. All of these methods return bool (True or False).
| Method | Condition for True | Example Returning True | Example Returning False |
|---|---|---|---|
isalpha() | All characters are alphabetic letters (A-Z, a-z, Unicode letters) | "Python" | "Python3", "Hello World" |
isalnum() | All characters are alphanumeric (letters or digits) | "Python3" | "Python 3", "item-1" |
isdecimal() | All characters are base-10 digits (0-9) | "2026" | "20.26", "2²" |
isdigit() | All characters are digits (includes superscripts/subscripts) | "2026", "²" | "20.26", "½" |
isnumeric() | All characters are numeric (includes fractions, Roman numerals) | "2026", "½" | "20.26", "abc" |
isspace() | All characters are whitespace (' ', \t, \n, \r, \f, \v) | " \t\n " | "", " a " |
islower() | At least one cased char, and all cased chars are lowercase | "python 3.10" | "Python", "123" |
isupper() | At least one cased char, and all cased chars are uppercase | "PCAP 2026" | "Pcap", "123" |
istitle() | String is titlecased (words start uppercase followed by lowercase) | "Hello World 2" | "Hello world", "HELLO" |
isidentifier() | String is a valid Python variable/identifier name | "var_1", "_test" | "2var", "my-var" |
isprintable() | All characters are printable (or string is empty) | "Hello 123" | "Hello\nWorld" |
5. The Digit Hierarchy: isdecimal() vs. isdigit() vs. isnumeric()
Understanding the exact Unicode subset relationship between digit methods is frequently tested:
# 1. Standard ASCII Digits ('0'-'9')
s_ascii = "123"
print(s_ascii.isdecimal(), s_ascii.isdigit(), s_ascii.isnumeric())
# Output: True True True
# 2. Superscript / Subscript digits ('²')
s_super = "2\u00b2" # '2²'
print(s_super.isdecimal(), s_super.isdigit(), s_super.isnumeric())
# Output: False True True
# 3. Vulgar Fractions ('½')
s_frac = "\u00bd" # '½'
print(s_frac.isdecimal(), s_frac.isdigit(), s_frac.isnumeric())
# Output: False False True
6. Case Predicate Rules and Uncased Characters
The case testing methods (islower(), isupper(), and istitle()) ignore non-cased characters (digits, symbols, whitespace, punctuation), provided there is at least one cased character in the string:
# Non-cased characters are ignored if a cased letter exists
print("python_3.10!".islower()) # True ('p', 'y', 't', 'h', 'o', 'n' are lowercase)
print("HTTP_404_NOT_FOUND".isupper()) # True
# If NO cased characters exist, islower() and isupper() return False
print("12345!".islower()) # False
print("12345!".isupper()) # False
print(" ".islower()) # False
7. The Empty String Rule
A critical rule for PCAP candidates to memorize:
The Universal False Rule on Empty Strings: Every character classification method (
isalpha(),isdigit(),isdecimal(),isnumeric(),isalnum(),isspace(),islower(),isupper(),istitle()) returnsFalsewhen invoked on an empty string"".Reason: All of these methods strictly require a string length of at least 1 character ($len \ge 1$) satisfying the condition.
(Exception:
"".isprintable()evaluates toTrue).
What happens when executing the following Python code snippet?
text = "automation"
pos = text.find("xyz")
print(pos)
What is the output of the following occurrence count?
print("banana".count("ana"))
Consider the following expressions evaluated on empty and mixed strings:
What is printed?print("".isalpha(), "123a".isdigit(), "Python 3".islower())
What exception, if any, is raised by the following code?
filename = "report.pdf"
print(filename.endswith([".pdf", ".docx"]))