String Operations: Joining, Repeating, Searching and Comparing
Concatenation, repetition, membership testing, comparison and iteration - the operations that work on strings because a string is a sequence.
- Basics
- Data Types
- Operators
- Strings
- Control Flow
- Lists
- Tuples
- Sets
- Dictionaries
- Comprehensions
- Functions
- Advanced Functions
- Recursion
- Exception Handling
- File Handling
- Modules
- Standard Library
- OOP
- Advanced OOP
- Iterators and Generators
- Decorators
- Context Managers
- Descriptors and Dataclasses
- Python Internals
- Concurrency
- Regular Expressions
- Serialization
- Command Line Python
- Testing and Debugging
- Type Hints
- Performance
- Python Security
- DSA with Python
Concatenation
first = "Meera"
last = "Nair"
print(first + " " + last) # Meera Nair
print(first "Nair") # SyntaxError in this form
print("Meera" "Nair") # MeeraNair - adjacent LITERALS join automaticallyAdjacent string literals are joined by the compiler. That is useful for splitting a long literal across lines, and it is a trap when a comma is left out of a list:
message = (
"This is a long message that "
"continues on the next line "
"without any + signs."
)
names = ["Meera", "Arun" "Sara"] # only TWO items: the last two joined
print(len(names)) # 2Concatenation in a loop is a mistake
parts = ["a", "b", "c", "d"]
# Slow: builds a brand new string on every iteration.
result = ""
for part in parts:
result += part
# Fast and idiomatic: one allocation.
result = "".join(parts)Because strings are immutable, += cannot extend in place; it copies everything built so far. For four items that is irrelevant, for forty thousand it is the difference between instant and unusable.
Repetition
print("-" * 30) # a separator line
print("ab" * 3) # ababab
print("=" * 0) # empty string, no error
print("x" * -2) # empty string too
print("Menu".center(30, "-")) # ------------Menu--------------Membership
text = "Python programming"
print("gram" in text) # True
print("Gram" in text) # False - case sensitive
print("gram" not in text) # False
# Case insensitive test
print("gram" in text.lower()) # Truein on a string is a substring test, not a character test. "th" in "Python" is True even though "th" is two characters.
Comparison
print("apple" == "apple") # True
print("apple" < "banana") # True
print("Apple" < "apple") # True - uppercase sorts first
print("abc" < "abd") # True - first differing character decides
print("abc" < "abcd") # True - a prefix sorts before the longer stringComparison walks the strings character by character and compares Unicode code points. Because every uppercase ASCII letter has a lower code point than every lowercase one, "Z" < "a" is True. Sort case insensitively on purpose when that matters:
names = ["banana", "Apple", "cherry"]
print(sorted(names)) # ['Apple', 'banana', 'cherry']
print(sorted(names, key=str.lower)) # ['Apple', 'banana', 'cherry'] by word
print(sorted(names, key=str.casefold)) # the most robust form for non English textIteration
text = "Python"
for character in text:
print(character, end="|")
print() # P|y|t|h|o|n|
for index, character in enumerate(text, start=1):
print(index, character)
vowels = sum(1 for ch in text.lower() if ch in "aeiou")
print(vowels) # 1Iterating over words and lines
sentence = "the quick brown fox"
for word in sentence.split():
print(word.capitalize())
document = "line one\nline two\nline three"
for line in document.splitlines():
print(">", line)Useful built ins that work on strings
text = "Python"
print(len(text)) # 6
print(min(text), max(text)) # P y - by code point
print(sorted(text)) # ['P', 'h', 'n', 'o', 't', 'y']
print("".join(sorted(text))) # Phnoty
print(list(reversed(text))) # ['n', 'o', 'h', 't', 'y', 'P']
print("".join(reversed(text))) # nohtyPA practical example
def is_palindrome(text):
cleaned = "".join(ch.lower() for ch in text if ch.isalnum())
return cleaned == cleaned[::-1]
print(is_palindrome("A man, a plan, a canal: Panama")) # True
print(is_palindrome("Python")) # False
def word_frequency(sentence):
counts = {}
for word in sentence.lower().split():
word = word.strip(".,!?;:")
counts[word] = counts.get(word, 0) + 1
return counts
print(word_frequency("the cat and the hat and the bat"))Common mistakes
- Building a string with
+=inside a large loop. - Forgetting a comma in a list of string literals, so two items silently merge.
- Comparing strings without normalising case when case is not meaningful.
- Using
+to mix a string and a number."Total: " + 5is aTypeError; use an f-string. - Assuming
sorted(text)returns a string. It returns a list of characters. - Assuming
"a" in textchecks characters only. It checks substrings of any length.
Best practices
- Use
"".join(parts)to assemble strings from many pieces. - Use f-strings to mix text and values rather than concatenation.
- Normalise with
.casefold()before comparing text that came from users. - Use bracket continuation for long literals rather than backslashes or
+.
Practice
- Explain why
["a", "b" "c"]has two elements and how to spot the bug in review. - Write a function that counts vowels and consonants in a sentence.
- Compare the running time of
+=concatenation andjoinfor 100 000 pieces, and describe the result. - Sort a list of names case insensitively without changing the stored capitalisation.
- Write a palindrome check that ignores punctuation, spacing and case.
Conclusion
Strings support the sequence operations - join with +, repeat with *, search with in, compare by code point, iterate by character. The only rule that changes how you write code is immutability, which is why join exists and why you should reach for it.