Python Part I · Module 4 · Strings
Joining and Repeating Strings
Two of the operators from Module 3 do something useful to text. Plus combines two strings into a longer one, and star repeats a string a given number of times — which is how you produce a divider line that is exactly as wide as the title above it. Both are simple, and both have edges worth knowing: plus adds nothing of its own, not even a space, and it refuses outright to combine text with a number. The chapter closes with the reason plus is the wrong tool once you have many pieces to assemble, an argument that comes directly from what Module 2 said about immutable values and sets up the join method later in this module.
Foundation11 min readPythonPart IFundamentalsStringsOperators
Part I · Module 4 · Chapter 4.3 · Joining and Repeating Strings
1. Plus Combines Two Strings
You already know + as addition. Applied to two strings it does something different but closely related: it produces a new string containing the characters of the first followed by the characters of the second. The word for that is concatenation.
name = "smoke" + "_test"
print(name)smoke_testIt works the same way on names rather than literals, which is how it is actually used:
prefix = "axi_"
kind = "smoke"
print(prefix + kind)axi_smokeAnd it chains, because the result of a + is itself a string that the next + can work on:
print("runs" + "/" + "axi_smoke")runs/axi_smokeThat is a normal Module 3 expression being evaluated left to right. There is nothing special happening: each + takes two strings and gives back one.
2. Plus Adds Nothing of Its Own
Here is the first thing people get wrong, and it is worth demonstrating rather than stating:
print("PASS" + "FAIL")PASSFAILNo space. Concatenation joins exactly the characters you gave it and inserts nothing. If you want a space, it has to be a character in one of the strings, or a string of its own:
print("PASS" + " " + "FAIL")PASS FAILThis is also the reason Section 6 of this chapter exists: once you are joining more than three or four pieces, putting the separators in by hand becomes both tedious and easy to get wrong. Chapter 4.9 has the tool for that case.
3. Plus Will Not Mix Text and Numbers
Chapter 3.2 already showed this, so it is a reminder rather than a lesson:
print("seed_" + 42)TypeError: can only concatenate str (not "int") to strPython will not guess. "seed_" is text and 42 is a number, and there is no single obvious meaning for combining them — so rather than inventing one, Python stops.
The fix is the conversion from Chapter 2.11: turn the number into text first, and then both operands are strings.
seed = 42
print("seed_" + str(seed))seed_42That works and is perfectly correct. It is also the exact pain that f-strings remove, which is why Chapter 4.12 exists — but str() plus concatenation is the honest foundation underneath it, and you should be able to read code written this way.
4. Star Repeats a String
The other Module 3 operator that does something useful to text is *. With a string on one side and a whole number on the other, it repeats the string:
rule = "-" * 40
print(rule)
print(len(rule))----------------------------------------
40Forty hyphens, from four characters of source. len() is Chapter 4.6; here it is only confirming the count.
The order of the operands does not matter:
print(3 * "ab")abababBoth forms are valid. "ab" * 3 reads more naturally to most people — the thing first, then how many times — and that is the form you will usually see.
The idiom worth keeping
Repetition becomes genuinely useful when the count comes from the text itself. This produces an underline that is always exactly as wide as the title, however the title changes:
title = "REGRESSION SUMMARY"
print(title)
print("=" * len(title))REGRESSION SUMMARY
==================Change the title and the underline follows automatically. Compare that with typing eighteen equals signs by hand, which is correct exactly once.
5. The Edges of Star
Three cases, all verified, each teaching something.
A count of zero gives empty text. Not an error, not a single copy:
print("[" + "-" * 0 + "]")[]A negative count also gives empty text. Python treats "repeat it minus five times" as "there is nothing to repeat":
print("[" + "-" * -5 + "]")[]That is worth knowing because it fails quietly. If a count is computed and comes out negative, you get an empty string rather than a complaint, and the missing text can be hard to trace.
The count must be a whole number. A float is refused even when it looks whole:
print("-" * 2.0)TypeError: can't multiply sequence by non-int of type 'float'print("-" * "5")TypeError: can't multiply sequence by non-int of type 'str'The word sequence in that message is Python's general term for "a thing with items in order", and a string is one. The message is saying: you can repeat a sequence a whole number of times, and 2.0 is not a whole number — it is a float. As Chapter 2.6 established, 2.0 and 2 are different types even though they compare equal. Convert with int() if a count arrives as a float.
Also note what * does not do: it repeats, it does not pad.
print("[" + "ab" * 2 + "]")[abab]If you want text padded out to a fixed width so that a column lines up, that is a formatting job, and Chapter 4.13 does it properly.
6. Why Plus Is the Wrong Tool for Many Pieces
You can build a string up a step at a time. Chapter 3.3 taught +=, and it works on text exactly as you would expect:
line = "vcs"
line += " -full64"
line += " -f files.f"
print(line)vcs -full64 -f files.fThat is fine. Three pieces, three lines, perfectly readable.
Now the part the canonical curriculum wants you to understand, because it is a real cost and not a style preference.
Recall from Chapter 2.13 that a string cannot be modified. So line += " -full64" is not appending anything to the existing string. It cannot. What actually happens is:
- a new string is built, long enough to hold both parts;
- every character of the old
lineis copied into it; - every character of
" -full64"is copied after them; - the name
lineis rebound to that new string; - the old string is now unused.
For three pieces that is trivial. But notice what the cost depends on: step 2 copies everything accumulated so far. Build a 500-line report one line at a time and the first line gets copied 500 times, the second 499 times, and so on. The work grows out of proportion to the amount of text — you are not doing 500 units of work, you are doing something closer to 125,000.
You will meet the tempting version of this mistake in Module 10, where repeating an action many times becomes easy. The reason not to do it is here, in immutability, and it is worth carrying forward: repeated concatenation is not slow because Python is slow. It is slow because each step rebuilds everything.
7. Predict, Run, Observe
Predict all five before reading on.
print("a" + "b" + "c")
print("ab" * 3)
print("-" * 0 + "end")
print(len("ab" * 4))
print("x" + "y" * 3)The verified output:
abc
ababab
end
8
xyyyThe first four should be straightforward. The fifth is the one to check yourself on: the answer is xyyy, not xyxyxy. Precedence decides it — Chapter 3.8 established that * binds tighter than +, so "y" * 3 happens first and the result is concatenated onto "x". If you want the other meaning you must say so:
print(("x" + "y") * 3)xyxyxyWhich is exactly the argument Chapter 3.8 made: where a reader would have to work out the order, put the brackets in.
8. Exercises
Exercise 1 — build a path. Using only +, build and print the string runs/axi_smoke/sim.log from three names: base = "runs", test = "axi_smoke", filename = "sim.log". Count the separators you had to supply yourself.
Exercise 2 — the missing space. This prints axi_smokePASS. Fix it two different ways — once by changing a literal, once by adding an operand:
test = "axi_smoke"
status = "PASS"
print(test + status)Exercise 3 — predict, then run. Give the output of each without running it:
(a) "=" * 5 (b) 5 * "=" (c) "=" * 1 (d) "ab" + "ab" (e) "ab" * 2 (f) len("--" * 10)
Exercise 4 — the underline. Write a two-line output where the second line underlines the first with -, and the underline is exactly the right width for any title. Then change the title to something longer and confirm you did not have to touch the second line.
Exercise 5 — read the error. A colleague's script fails with TypeError: can only concatenate str (not "int") to str on this line. Explain which operand is at fault and give two different corrected versions:
print("runtime: " + 182)Exercise 6 — explain the cost. In two or three sentences, and without using the word "loop", explain why assembling a thousand-line report with += does much more work than assembling a three-line one, and name the method this module provides instead.
9. Summary
+on strings concatenates: the characters of the left operand followed by those of the right.+inserts nothing of its own. Every separator must be a character you supplied.+will not mix text and numbers —TypeError: can only concatenate str (not "int") to str. Convert withstr()first, as Chapter 2.11 showed.*repeats a string a whole number of times, in either operand order."=" * len(title)is the idiom worth keeping — an underline derived from the text rather than typed out.- A zero or negative count gives an empty string, quietly. A float or string count is a
TypeError. *repeats, it does not pad. Padding to a fixed width is Chapter 4.13.*binds tighter than+, so"x" + "y" * 3isxyyy. Bracket it if a reader would have to think.- Each
+=rebuilds the whole string, because strings are immutable. Fine for a few pieces; the wrong tool for many.joinin Chapter 4.9 is the right one.
You can now write text and combine it. The next chapter goes the other way, and reaches inside a string to a single character.
Where this fits
Part of the Python curriculum.
