The str.rpartition(sep) method splits a string at the last (rightmost) occurrence of a designated separator and returns a 3-element tuple: (head, sep, tail).
The Absence Asymmetry Rule: If the separator is found, head contains everything before the final occurrence, and tail contains everything after it. If the separator is absent, rpartition() returns ("", "", original_string)—placing the full string into tail, in stark contrast to partition() which places it into head.
The formal method signature defined in Python's standard library is:
str.rpartition(sep)
| Parameter | Type | Default | Description |
|---|---|---|---|
sep |
str |
Required | The delimiter string to locate from right to left. Must be a non-empty string. Passing "" raises a ValueError. |
tuple[str, str, str]. This makes rpartition() completely immune to unpacking length errors.
When multiple occurrences of the separator exist, rpartition() anchors on the final (rightmost) match:
filepath = "home/spark/projects/python/analysis.py"
# Splits on the final slash '/'
directory, sep, filename = filepath.rpartition("/")
print("Directory: ", repr(directory))
print("Separator: ", repr(sep))
print("Filename: ", repr(filename))
Expected Output:
Directory: 'home/spark/projects/python'
Separator: '/'
Filename: 'analysis.py'
Notice how directory preserves all preceding slashes and directories intact without requiring array indexing or loop iterations.
Understanding the difference between partition() and rpartition() is essential when handling absent separators:
text = "alpha.beta.gamma"
# 1. Delimiter present
print("partition('.'): ", text.partition("."))
print("rpartition('.'): ", text.rpartition("."))
# 2. Delimiter absent: Note where the original text lands!
plain = "standalone_word"
print("\nWhen delimiter is missing:")
print("partition(':'): ", plain.partition(":")) # Position 0 (head)
print("rpartition(':'): ", plain.rpartition(":")) # Position 2 (tail)
Expected Output:
partition('.'): ('alpha', '.', 'beta.gamma')
rpartition('.'): ('alpha.beta', '.', 'gamma')
When delimiter is missing:
partition(':'): ('standalone_word', '', '')
rpartition(':'): ('', '', 'standalone_word')
| Scenario | str.partition(sep) |
str.rpartition(sep) |
|---|---|---|
| Split Anchor | First occurrence (leftmost) | Last occurrence (rightmost) |
| When Found | (head, sep, tail) |
(head, sep, tail) |
| When Absent | (original, "", "") (in head) |
("", "", original) (in tail) |
| Presence Check | bool(sep) |
bool(sep) |
Both methods inspect text from the right, but their return structures serve different programming paradigms:
| Feature | str.rpartition(sep) |
str.rsplit(sep, 1) |
|---|---|---|
| Output Container | 3-tuple |
list (1 or 2 items) |
| Unpacking Safety | Safe (fixed 3 elements) | Fragile (raises ValueError if absent) |
| Separator Retained? | Yes (at index 1) | No (discarded) |
| Mutability | Immutable tuple | Mutable list |
Extracting base file names and extensions without third-party libraries, including handling dotfiles:
def get_file_extension(filename: str) -> tuple[str, str]:
stem, sep, ext = filename.rpartition(".")
if not sep or not stem:
# No extension or dotfile like '.env'
return filename, ""
return stem, ext
print("Normal file: ", get_file_extension("archive.tar.gz"))
print("No extension:", get_file_extension("Makefile"))
print("Dotfile: ", get_file_extension(".gitignore"))
Expected Output:
Normal file: ('archive.tar', 'gz')
No extension: ('Makefile', '')
Dotfile: ('.gitignore', '')
Dynamically importing classes or isolating callable names from fully qualified module strings:
def split_module_qualname(target: str) -> tuple[str, str]:
package, sep, cls_name = target.rpartition(".")
return (package, cls_name) if sep else ("", target)
path1 = "urllib.request.urlopen"
path2 = "StandaloneFunction"
print("Qualified target:", split_module_qualname(path1))
print("Global target: ", split_module_qualname(path2))
Expected Output:
Qualified target: ('urllib.request', 'urlopen')
Global target: ('', 'StandaloneFunction')
Separating host addresses from port numbers while maintaining safe fallback defaults:
def parse_host_port(address: str, default_port: int = 80) -> tuple[str, int]:
host, sep, port_str = address.rpartition(":")
if sep and port_str.isdigit():
return host, int(port_str)
return address, default_port
print("With port: ", parse_host_port("database.internal.net:5432"))
print("Without port:", parse_host_port("api.plus2net.com", 443))
Expected Output:
With port: ('database.internal.net', 5432)
Without port: ('api.plus2net.com', 443)
Just like partition(), rpartition() does not allow an empty string separator:
# Raises ValueError: empty separator
"data".rpartition("")
When writing fallback logic, remember that if the separator is missing, the source string is located at index 2 (tail), not index 0 (head):
head, sep, tail = "token".rpartition(":")
print("head:", repr(head)) # ''
print("tail:", repr(tail)) # 'token'
Below is a production-grade utility class, PathPartitioner, demonstrating file path decomposition, extension resolution, and automated unit assertions:
class PathPartitioner:
@staticmethod
def split_path(filepath: str) -> tuple[str, str]:
"""Splits into directory and filename."""
dirname, sep, basename = filepath.rpartition("/")
return (dirname, basename) if sep else ("", filepath)
@staticmethod
def split_extension(filename: str) -> tuple[str, str]:
"""Splits into stem and extension."""
stem, sep, ext = filename.rpartition(".")
return (stem, ext) if sep else (filename, "")
@staticmethod
def get_terminal_segment(text: str, sep: str) -> str:
"""Returns only the trailing portion after the last sep."""
_, _, tail = text.rpartition(sep)
return tail
# ==========================================
# SELF-CHECKING UNIT ASSERTIONS
# ==========================================
partitioner = PathPartitioner()
# 1. Separator present: rightmost match
h, s, t = "a-b-c-d".rpartition("-")
assert h == "a-b-c"
assert s == "-"
assert t == "d"
# 2. Separator absent: string in tail
h_abs, s_abs, t_abs = "abc".rpartition("-")
assert h_abs == ""
assert s_abs == ""
assert t_abs == "abc"
# 3. Path parsing
d, f = partitioner.split_path("/var/log/nginx/access.log")
assert d == "/var/log/nginx"
assert f == "access.log"
d2, f2 = partitioner.split_path("readme.txt")
assert d2 == ""
assert f2 == "readme.txt"
# 4. Extension parsing
stem, ext = partitioner.split_extension("backup.2026.tar.gz")
assert stem == "backup.2026.tar"
assert ext == "gz"
# 5. Terminal segment extraction
assert partitioner.get_terminal_segment("pkg.module.Class", ".") == "Class"
# 6. Empty separator exception
try:
"sample".rpartition("")
assert False, "Expected ValueError not raised"
except ValueError as e:
assert "empty separator" in str(e)
print("Parsed path: ", d, "->", f)
print("Parsed extension:", stem, "->", ext)
print("All str.rpartition() unit assertions passed successfully!")
Open in Google Colab View on GitHub
Run all right-to-left partition examples interactively, contrast rpartition() with partition(), extract file extensions, and resolve module paths in Google Colab.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.