PYTHON / STRINGS
Splitting and joining strings
Cut strings into lists with split(), rsplit(), partition() and splitlines(), and rebuild them with sep.join() without hitting type errors.
What you will learn
- Use split() for runs of whitespace, split(sep) for an exact separator
- Limit the number of cuts with maxsplit, rsplit, or partition
- Rebuild strings with sep.join(pieces): the separator owns the method
- Convert non-strings first: ','.join(str(x) for x in numbers)
Understanding Splitting and joining strings
Think of split as scissors: you name a separator, Python cuts at every occurrence of it, and the separator itself is thrown away. That means n separators always produce n+1 pieces, so adjacent separators leave an empty string between them and a leading or trailing separator leaves an empty string at the edge. This is why "a,b,,c".split(",") gives four items including one empty string rather than silently skipping the gap.
Calling split() with no argument switches to a different rule entirely: any run of whitespace (spaces, tabs, newlines) counts as one separator, and leading and trailing whitespace produce no empty fields. That mode is what you want for human-typed text with uneven spacing; the explicit split(sep) mode is what you want for machine formats such as CSV lines, where an empty field is real data you must not lose.
join is the inverse operation, but the roles are swapped: you call it on the separator string and pass the pieces as an iterable, as in ", ".join(parts). Every item must already be a str, because join concatenates bytes of text and refuses to guess how to render an int. It also builds the result in one allocation after measuring the total length, which is why join is the standard way to assemble a string from many parts instead of repeatedly appending with +.
line = " ripe banana split "
print(line.split())
print(line.split(" "))
print("a,b,,c".split(","))
print("a,b,c,d".split(",", 1))
print("a,b,c,d".rsplit(",", 1))
fields = ["2024-05-01", "sensor-7", "21.5"]
row = ",".join(fields)
print(row)
print(row.split(",") == fields)split cuts a string at a separator and join is its inverse, but the separator is an argument to split and is the object that owns join.
Worked examples
Joining values that are not strings
Shows that join refuses non-string items and how a generator expression fixes it.
values = [1, 2, 3]
try:
print("-".join(values))
except TypeError as e:
print("TypeError:", e)
print("-".join(str(v) for v in values))
print("-".join("abc"))Example explained
Line 1join never calls str() on your behalf, and the error message names the index of the first bad item.
Line 2The generator expression converts each value as join consumes it, so no intermediate list is created.
Line 3A str is itself an iterable of characters, so "-".join("abc") inserts the separator between letters.
partition when the separator may be missing
Compares partition with split(sep, 1) for parsing key=value text where the key sometimes has no value.
for setting in ["path=/usr/bin=extra", "verbose"]:
key, sep, value = setting.partition("=")
print(repr(key), repr(sep), repr(value))
print(setting.split("=", 1))Example explained
Line 1partition always returns exactly three items, so unpacking into three names can never raise ValueError.
Line 2The middle item is the separator itself, so an empty sep is how you detect that it was absent.
Line 3split("=", 1) cuts only at the first "=", but returns a one-item list when there is no "=", which would break unpacking.
splitlines versus split on a newline
Demonstrates why splitlines is the right tool for multi-line text with mixed line endings.
text = "alpha\nbeta\r\ngamma\n"
print(text.splitlines())
print(text.split("\n"))
print("|".join(text.splitlines()))Example explained
Line 1splitlines recognises \r\n as one ending and does not emit an empty field for the final newline.
Line 2split("\n") is purely literal, so \r stays attached to 'beta' and the trailing newline yields ''.
Line 3Joining the splitlines result normalises the text: the original \r\n ending is gone.
Important notes
text.split("") is an error (ValueError: empty separator); to get a list of single characters use list(text).
join takes any iterable of strings, so ",".join({"a": 1, "b": 2}) joins the dict's keys and produces 'a,b', which is rarely the intent.
Common mistakes
Writing fields.join(",") instead of ",".join(fields), which raises AttributeError: 'list' object has no attribute 'join'.
Using split(" ") on hand-typed text: double spaces produce '' entries and tabs are not split at all, so a later int() or float() on a field raises ValueError.
Forgetting that split(sep) keeps empty fields, so "a,,b".split(",") has three items and code that assumes two ends up indexing the wrong field.
Try it yourself
Change, predict, then run
Starting from raw = " tomato; ; basil ;mozzarella ; ", split on ";", strip each piece, drop the empty ones, and join what remains with ", " so the program prints exactly 'tomato, basil, mozzarella'.
Open the Python workspaceCheck your understanding
Why does " a b ".split() return ['a', 'b'] while " a b ".split(" ") returns ['', 'a', 'b', '']?
- split() with no argument treats any run of whitespace as a single separator and produces no empty edge fields, while split(" ") cuts at every individual space character
- split() is just shorthand for .strip().split(" "), so only the outer spaces differ
- split(" ") adds empty strings as markers for where the original string began and ended
- split() returns a tuple rather than a list, and tuples cannot contain empty strings
Show answer
The no-argument form uses a distinct whitespace mode: runs collapse and leading/trailing whitespace is ignored. Option 2 is tempting but fails on interior gaps: "a b".split() gives ['a', 'b'], whereas "a b".strip().split(" ") gives ['a', '', 'b'], and split() also cuts on tabs and newlines that split(" ") ignores.