For AI agents: use /llms.txt for the Nakafa content index.
Python strings are immutable: the value stored in a string object cannot be edited in place. An operation may create another string, and a variable may then be rebound to that new object, but the original string object remains unchanged.
Assigning to a character position therefore raises TypeError. Lists are different because their elements can be replaced in place.
# Demonstration of immutable string nature
s = "hello"
print(f"Initial string: {s}")
# Trying to change character directly
try:
s[1] = "a" # This will cause an error
except TypeError as e:
print(f"Error: {e}")
# The correct way: creating new string
s_new = s[:1] + "a" + s[2:]
print(f"New string: {s_new}")
# Original string remains unchanged
print(f"Original string still: {s}")Slicing and concatenation are two ways to build a new string from an existing one. The + operator does not append to the original object.
For a Python string, len() counts Unicode code points. An ordinary Latin letter, space, or punctuation mark usually contributes one, but a single visible grapheme can contain several code points, as happens with some emoji and combining marks.
# Measuring length of various string types
strings = [
"hello",
"Hello World!",
"Python programming",
"", # empty string
" ", # string containing spaces
"123456"
]
for s in strings:
print(f"'{s}' has length: {len(s)}")
# String length with special characters
special = "Hello\nWorld\t!"
print(f"String with escape sequence: {len(special)} characters")
print(f"String display: {repr(special)}")
raw_newline = r"\n"
print(f"Raw \n spelling: {len(raw_newline)} code points")In a normal string literal, the source spellings \n and \t each become one newline or tab code point. In a raw string such as r"\n", the backslash and n remain two separate code points.
Python provides several case-conversion methods. Each returns a new string and applies Unicode casing rules.
# Example of case transformation on strings
text = "Python Programming Language"
# Convert to all uppercase
upper_text = text.upper()
print(f"Upper: {upper_text}")
# Convert to all lowercase
lower_text = text.lower()
print(f"Lower: {lower_text}")
# Title case: first letter of each word capitalized
title_text = text.title()
print(f"Title: {title_text}")
# Example with mixed string
mixed = "hELLo WoRLd PyThOn"
print(f"Original: {mixed}")
print(f"Title case: {mixed.title()}")
# Title case on string with numbers and symbols
complex_text = "python3.9 is-awesome!"
print(f"Complex title: {complex_text.title()}")The title() method follows a mechanical casing rule. It can be convenient for display labels, but it is not a reliable formatter for people's names or editorial titles. Preserve authoritative spelling or apply domain-specific rules for those values.
With no argument, strip(), lstrip(), and rstrip() remove surrounding whitespace. A chars argument is treated as a set of individual characters to remove repeatedly from the relevant ends, not as one exact prefix or suffix.
# Demonstration of various strip methods
text = " Hello World "
print(f"Original: '{text}'")
# Remove spaces on both sides
stripped = text.strip()
print(f"Strip: '{stripped}'")
# Remove spaces only on the left
left_stripped = text.lstrip()
print(f"Left strip: '{left_stripped}'")
# Remove spaces only on the right
right_stripped = text.rstrip()
print(f"Right strip: '{right_stripped}'")
# Strip with special characters
special_text = "+++Hello World+++"
custom_stripped = special_text.strip("+")
print(f"Custom strip: '{custom_stripped}'")
# Strip multiple characters
messy_text = "---+++Hello World+++---"
clean_text = messy_text.strip("-+")
print(f"Multiple char strip: '{clean_text}'")
# Remove one exact suffix
filename = "report.txt"
print(f"Exact suffix removal: '{filename.removesuffix('.txt')}'")Use removeprefix() or removesuffix() when you need to remove one exact token. This distinction prevents a character set from stripping more than intended.
The index() and find() methods are used to search for substring positions in strings. The difference lies in how they handle cases when the substring is not found.
# Substring search with various methods
text = "Python is a powerful programming language"
# Using index() - will error if not found
try:
pos = text.index("powerful")
print(f"'powerful' found at position: {pos}")
# Search for non-existent substring
pos_not_found = text.index("Java")
except ValueError as e:
print(f"Error with index(): {e}")
# Using find() - returns -1 if not found
pos_find = text.find("powerful")
print(f"find('powerful'): {pos_find}")
pos_not_found = text.find("Java")
print(f"find('Java'): {pos_not_found}")
# Use membership when only presence matters
print(f"Contains 'Python': {'Python' in text}")
# Search with start and end parameters
substring = "programming"
start_pos = text.find(substring, 20) # start from position 20
print(f"'{substring}' after position 20: {start_pos}")
# Count substring occurrences
count = text.count("a")
print(f"Letter 'a' appears {count} times")Choose the method according to the failure contract: find() returns -1, while index() raises ValueError. If you only need a yes-or-no answer, the in operator is clearer than either position method.
Python provides predicates that classify the code points in a string. They answer questions about character categories, not whether the value satisfies a complete business format.
# Example of validating various character types
test_strings = [
"Python",
"Python3",
"12345",
"Hello World",
"hello123",
"UPPERCASE",
"lowercase",
"",
" "
]
print("String\t\tAlpha\tDigit\tAlnum\tUpper\tLower")
print("-" * 60)
for s in test_strings:
alpha = s.isalpha()
digit = s.isdigit()
alnum = s.isalnum()
upper = s.isupper()
lower = s.islower()
print(f"'{s:<10}'\t{alpha}\t{digit}\t{alnum}\t{upper}\t{lower}")
# Practical example for input validation
def validate_username(username):
if not username:
return "Username cannot be empty"
if not username.isascii() or not username.isalnum():
return "Username can only contain ASCII letters and numbers"
if len(username) < 3:
return "Username must be at least 3 characters"
return "Username is valid"
# Test validation
usernames = ["user123", "user@123", "ab", "ValidUser"]
for username in usernames:
result = validate_username(username)
print(f"'{username}': {result}")Methods such as isdigit() and isalnum() are Unicode-aware, and the empty string makes them return False. A phone number, postal code, or username still needs explicit rules for alphabet, length, separators, prefixes, and any other domain constraints.
The startswith() and endswith() methods allow you to check if a string starts or ends with a specific substring.
# Example usage of startswith and endswith
files = [
"document.pdf",
"image.jpg",
"script.py",
"data.csv",
"README.md",
"backup.zip"
]
# Check file extensions
print("Files with specific extensions:")
for file in files:
if file.endswith(".py"):
print(f"Python file: {file}")
elif file.endswith((".jpg", ".png", ".gif")):
print(f"Image file: {file}")
elif file.endswith(".pdf"):
print(f"PDF file: {file}")
# Check prefixes
urls = [
"https://www.google.com",
"http://example.com",
"ftp://files.example.com",
"mailto:user@example.com"
]
print("\nURL analysis:")
for url in urls:
if url.startswith("https://"):
print(f"Secure web: {url}")
elif url.startswith("http://"):
print(f"Web: {url}")
elif url.startswith("ftp://"):
print(f"FTP: {url}")
elif url.startswith("mailto:"):
print(f"Email: {url}")
# Using tuple for multiple prefix/suffix
text = "Hello World"
print(f"\nStarts with greeting: {text.startswith(('Hi', 'Hello', 'Hey'))}")Both methods support tuples as parameters, allowing checking of multiple prefixes or suffixes simultaneously.
The replace() method is used to replace all occurrences of a substring with a new string.
# Example usage of replace method
text = "Python is great. Python is powerful. Python is easy."
print(f"Original: {text}")
# Replace all occurrences
replaced = text.replace("Python", "Java")
print(f"Replace all: {replaced}")
# Replace with count limit
limited_replace = text.replace("Python", "Java", 2)
print(f"Replace 2 first: {limited_replace}")
# Replace with empty string (remove)
no_dots = text.replace(".", "")
print(f"Remove dots: {no_dots}")
# Chaining replace for multiple replacements
sentence = "I love cats and dogs"
animal_replace = sentence.replace("cats", "birds").replace("dogs", "fish")
print(f"Chain replace: {animal_replace}")
# Case-sensitive replace
case_text = "Hello hello HELLO"
case_replace = case_text.replace("hello", "hi")
print(f"Case sensitive: {case_replace}")
# Practical example: cleaning phone number
phone = "+62-812-3456-7890"
clean_phone = phone.replace("-", "").replace("+", "")
print(f"Clean phone: {clean_phone}")The replace() method is case-sensitive and replaces all occurrences unless limited by the third parameter.
The split() method breaks a string into a list around a specified delimiter. With no delimiter, it treats consecutive whitespace as one separator and omits leading or trailing empty fields.
# Various ways to use split
text = "apple,banana,orange,grape"
print(f"Original: {text}")
# Split with comma delimiter
fruits = text.split(",")
print(f"Split by comma: {fruits}")
# Split without parameter (default whitespace)
sentence = "Python is a great language"
words = sentence.split()
print(f"Split by space: {words}")
# Split with limit
limited_split = text.split(",", 2)
print(f"Split max 2: {limited_split}")
# Split with multiple whitespace
messy_text = "word1 word2\t\tword3\n\nword4"
clean_split = messy_text.split()
print(f"Clean split: {clean_split}")
# Simple comma-separated fields without quoting
csv_line = "John,25,Engineer,New York"
data = csv_line.split(",")
name, age, job, city = data
print(f"Name: {name}, Age: {age}, Job: {job}, City: {city}")
# Split with non-existent character
no_delimiter = "no-delimiter-here"
result = no_delimiter.split(",")
print(f"No delimiter found: {result}")
# Joining back with join
joined = " | ".join(fruits)
print(f"Joined with pipe: {joined}")Use split() only when the delimiter rules are genuinely simple. It does not understand quoted CSV fields, escaping, or platform-specific paths. Use Python's csv module for CSV and pathlib for filesystem paths.
Because string methods return new strings, several transformations can be chained in one expression. Keep a chain short enough that each step remains obvious; name intermediate values when validation or explanation belongs between steps.
# Example of method chaining on strings
messy_input = " +++ Python Programming for Beginners --- "
print(f"Input: '{messy_input}'")
# Chaining multiple methods
clean_result = (messy_input
.strip() # remove spaces at start/end
.strip("+-") # remove + and - characters
.lower() # convert to lowercase
.replace("beginners", "experts")) # replace word
print(f"Clean result: '{clean_result}'")
# Example chaining for display-label formatting
labels = [" GETTING STARTED ", " array BASICS ", " DATA cleaning "]
formatted_labels = []
for label in labels:
formatted = label.strip().title()
formatted_labels.append(formatted)
print("Formatted labels:")
for label in formatted_labels:
print(f"- {label}")
# Chaining in list comprehension
sentences = [
" hello WORLD ",
" PYTHON programming ",
" DATA science "
]
processed = [s.strip().title() for s in sentences]
print(f"Processed sentences: {processed}")
# Normalize the domain without silently deleting invalid whitespace
def normalize_email(email):
cleaned = email.strip()
if any(char.isspace() for char in cleaned) or cleaned.count("@") != 1:
raise ValueError("Email must contain one @ and no whitespace")
local, domain = cleaned.split("@", 1)
if not local or not domain:
raise ValueError("Email needs both local and domain parts")
return f"{local}@{domain.lower()}"
emails = [" USER@EXAMPLE.COM ", "Test.User@Gmail.com", "admin@site.org"]
for email in emails:
processed = normalize_email(email)
print(f"'{email}' -> '{processed}'")Method chaining works well for a short linear cleanup. It is not automatically clearer than named steps.
The following bounded examples combine string methods with explicit assumptions. They illustrate mechanics, not universal production validators or parsers.
# Practical string method applications in various scenarios
# 1. Phone number validation and formatting
def format_phone_number(phone):
# Accept only ASCII digits and the separators supported here
allowed_separators = {" ", "-", "(", ")", "+"}
if any(char not in "0123456789" and char not in allowed_separators for char in phone):
return "Invalid character in phone number"
digits_only = "".join(char for char in phone if char in "0123456789")
# Format according to Indonesian standard
if len(digits_only) == 13 and digits_only.startswith("62"):
return f"+{digits_only[:2]}-{digits_only[2:5]}-{digits_only[5:9]}-{digits_only[9:]}"
elif len(digits_only) == 12 and digits_only.startswith("08"):
return f"{digits_only[:4]}-{digits_only[4:8]}-{digits_only[8:]}"
else:
return "Invalid number format"
phone_numbers = ["081234567890", "+6281234567890", "0812-3456-7890"]
for phone in phone_numbers:
formatted = format_phone_number(phone)
print(f"'{phone}' -> '{formatted}'")
# 2. Extract information from filename
def analyze_filename(filename):
# Separate name and extension
if "." in filename:
name, ext = filename.rsplit(".", 1)
ext = ext.lower()
else:
name, ext = filename, ""
# Analyze file type
image_exts = ["jpg", "jpeg", "png", "gif", "bmp"]
doc_exts = ["pdf", "doc", "docx", "txt"]
code_exts = ["py", "js", "html", "css", "java"]
if ext in image_exts:
file_type = "Image"
elif ext in doc_exts:
file_type = "Document"
elif ext in code_exts:
file_type = "Code"
else:
file_type = "Unknown"
return {
"name": name,
"extension": ext,
"type": file_type,
"length": len(filename)
}
files = ["photo.jpg", "report.pdf", "script.py", "data", "image.PNG"]
for file in files:
info = analyze_filename(file)
print(f"{file}: {info}")
# 3. Simple log file parser
def parse_log_entry(log_line):
# Format: [TIMESTAMP] LEVEL: MESSAGE
if not log_line.strip():
return None
# Extract timestamp
if log_line.startswith("[") and "]" in log_line:
end_bracket = log_line.index("]")
timestamp = log_line[1:end_bracket]
rest = log_line[end_bracket + 1:].strip()
else:
timestamp = "Unknown"
rest = log_line.strip()
# Extract level
if ":" in rest:
level, message = rest.split(":", 1)
level = level.strip()
message = message.strip()
else:
level = "INFO"
message = rest
return {
"timestamp": timestamp,
"level": level,
"message": message
}
log_entries = [
"[2023-09-17 10:30:15] ERROR: Database connection failed",
"[2023-09-17 10:30:16] INFO: Retrying connection",
"WARNING: Low disk space",
""
]
print("\nLog parsing results:")
for entry in log_entries:
parsed = parse_log_entry(entry)
if parsed:
print(f"Time: {parsed['timestamp']}, Level: {parsed['level']}, Message: {parsed['message']}")
# 4. URL slug generator from title
def create_slug(title):
# Convert to lowercase and replace spaces with dash
slug = title.lower()
# Replace non-alphanumeric characters with dash
clean_slug = ""
for char in slug:
if char.isalnum():
clean_slug += char
else:
if clean_slug and clean_slug[-1] != "-":
clean_slug += "-"
# Remove dashes at start and end
return clean_slug.strip("-")
titles = [
"Learn Python for Beginners",
"Tips & Tricks Programming",
"Data Science: Complete Guide",
"Machine Learning 101"
]
print("\nSlug generation:")
for title in titles:
slug = create_slug(title)
print(f"'{title}' -> '{slug}'")These four examples combine classification, searching, splitting, and replacement under small, stated formats. A production phone parser, filename parser, log parser, or slug generator also needs a domain contract, error policy, and tests for ambiguous input.
The key is to match each method to its exact semantics instead of treating a convenient string operation as complete validation.