Why it matters
Almost every program handles text. A login form reads a user name, a search box compares two words, a file parser splits a line into fields, and a report prints a sentence built from parts. In Python, text lives in an object of type str, and nearly every interview question about text starts with one. The rules that surprise beginners are small: positions start at 0, == compares the characters while is compares object identity, and a method that seems to change a string actually returns a new one. If you know these rules well, the harder string problems in this module become a matter of patterns rather than guesswork.
This lesson is the foundation for the other three. You will learn what a str is, how its positions work, which methods you will use every day, why immutability matters and how to read the error messages that appear when you get something wrong. You need no earlier Python beyond the idea of a variable and a print call.
The idea: text is a row of numbered boxes
A string is a sequence of characters. A character is one symbol: a letter, a digit, a space or a punctuation mark. Python has no separate character type. A single character is just a str of length 1, so "a" and "abc" have the same type.
Think of a row of numbered lockers. Each locker holds one character, and the lockers are numbered from 0. The number on a locker is its index. The first locker is index 0, not index 1. The last locker of a string of length 12 has index 11, because 12 lockers are numbered 0 through 11. Python also lets you count from the end: index -1 is the last locker, -2 is the one before it, and so on.
A string is immutable. Immutable means "cannot be changed after it is made". Picture a printed ticket. If you need a different ticket, the printer makes a new one; nobody erases the letters on the old one. A Python string behaves the same way. Once the object exists, its characters never change. Every method that seems to change a string builds and returns a new string, and the old one stays exactly as it was.
These two ideas, numbered positions and immutability, explain almost everything in this lesson.
Creating strings
There are three common ways to make a string.
a = "hello" # a string literal in double quotes
b = 'hello' # single quotes make the same kind of object
c = "hel" + "lo" # + joins two strings and builds a new oneAll three hold the same characters, and they are equal. Use whichever quote style avoids escaping inside the text. Use "it's" rather than 'it\'s'.
An empty string has length 0 and no characters. It is written "", two double quotes with nothing between them. Do not confuse it with None, which means "no value at all". An empty string is a real string that holds nothing. None is the absence of a string. The difference matters because calling a method on None raises an error, while len("") returns 0.
Worked example 1: a tour of everyday methods
The first program calls the methods you will use most. Read it line by line and predict each result before you run it. Then compare your prediction with the output below.
The program takes no input. The output is:
12
H
World
Hello
4
8
8
True
True
False
HeLLo, WorLd
padded|
ababab
True True False
Hello, WorldHere is what each line does.
len(s)counts the characters.H,e,l,l,o,,, the space,W,o,r,l,dmake 12.lenis a function, not a method, so you writelen(s)and nots.len().s[0]returns the character at index 0, which isH. Python has no separate character type, so the result is a string of length 1.s[7:]returns everything from index 7 to the end. Index 7 isW, so the answer isWorld.s[0:5]returns the characters from index 0 up to but not including index 5. That is indexes 0, 1, 2, 3 and 4, which spellHello. The end index is exclusive: it marks the first position you do not want. This slice syntax is called slicing.s.find("o")returns the index of the firsto, which is 4.s.find("o", 5)starts searching at index 5, so it skips the firstoand finds the one at index 8.s.rfind("o")searches from the right and finds the lasto, which is again index 8."World" in stests for a piece of text. It returnsTrueorFalse.startswithandendswithdo the same check at one end.s.replace("l", "L")returns a new string with everylchanged toL. The originalsis untouched.strip()removes whitespace from both ends. It removes spaces, tabs and line breaks."ab" * 3joins three copies ofab."".isspace()isFalse, becauseisspace()needs at least one character and all of them must be whitespace." ".isspace()isTrue.
When you read a method's documentation, look for three things: what it returns, whether the end index is included, and what happens when the input is out of range. In Python, find returns -1 when nothing matches, while indexing and in behave differently. Those three facts answer most of the questions you will meet.
Worked example 2: indexes and slice ranges
Positions are the source of most string bugs, so it is worth drawing them. Write the string on a line and put each index under its character:
index: 0 1 2 3 4 5 6 7 8 9 10 11
char: H e l l o , _ W o r l dThe slice s[0:5] takes the characters under indexes 0 to 4, which is Hello. The slice s[7:] takes the characters under indexes 7 to 11, which is World. The character at index 5 is the comma, and it belongs to neither range.
Use this rule every time: the number of characters in s[a:b] is b - a, as long as both numbers are inside the string. For s[0:5] that is 5 - 0 = 5 characters. For s[7:12] it would be 12 - 7 = 5 characters, which is World. The end index may equal the length, but it may not be bigger for a plain index. Slices are more forgiving, as the next section shows.
Now consider the last character. Its index is len(s) - 1, and Python also lets you write s[-1]. A loop that visits every character runs from 0 up to, but not including, len(s):
for i in range(len(s)):
print(s[i])range(len(s)) produces the numbers 0, 1, ..., up to len(s) - 1. If you ask for range(len(s) + 1), the last pass asks for index 12 in a string of length 12. That raises an IndexError, and you will see that message later in this lesson.
For most loops over text, you do not need an index at all. The next form is the Python way:
for ch in s:
print(ch)Use the index form only when you need the position.
Tracing a slice by hand
Trace "banana"[2:5] step by step to see the rule working.
- The string is
b a n a n a, with indexes 0 to 5. - The start index is 2, which is the first
n. - The end index is 5, which is excluded.
- Collect indexes 2, 3 and 4:
n,a,n. - The result is
"nan".
Do the same trace for "banana"[3:]. You start at index 3 and run to the end: a, n, a. The result is "ana". Tracing on paper is slow at first, but it is the fastest way to find an off-by-one bug. When your code is wrong, write the indexes above the characters and walk through the calls.
Comparing strings: == versus is
This is the most important rule in the lesson. The operator == asks whether two strings hold the same characters. The operator is asks whether two names point to the very same object in memory. These are different questions.
The output is:
True
True
True
False
True
True
True
97 98
3Read the first two lines carefully. a == b is True, and so is a is b. Here CPython, the standard Python program, reuses one object for both literals. The a is b result depends on that detail, and the next section explains why you should not rely on it. c is built at run time by "".join, so it is a new object. a == c is True because the characters match, and a is c is False because the two names point at different objects.
For text comparison, always use ==. Use .lower() or .casefold() on both sides when letter case should not matter. Do not use is for text. The word is is for checking against None, as in if name is None. It also breaks for text read from a user, a file or a network, because those strings are new objects.
Python also compares strings in order. "apple" < "banana" is True because a comes before b. Comparison goes character by character. At the first difference, the character with the smaller code wins. The code of a is 97 and the code of b is 98. When one string is a prefix of the other, the shorter one comes first: "pea" < "pear" is True. Python returns True or False for < and >. It does not return the numbers -1, 0 and 1 that some languages use. The function sorted uses these same comparisons, so a list of words sorts in dictionary order without extra code.
What happens inside: interning and identity
Why does a is b print True when b was written separately? CPython keeps a shared table of some strings, and when a short string literal appears in one place, the compiler can reuse an object that already exists. This is called interning. Interning is an optimization, and the rules for which strings are shared are not part of the language. Two strings can be equal and still be different objects, and two equal literals can be the same object or not depending on how the code was compiled.
The picture in words: a and b both point at one shared "hi". The variable c points at a second "hi" that was built at run time. Python's id function shows the address of an object. Two names with the same id point at the same object. Your output will show different numbers, so do not compare them with printed values from this page.
a = "hi"
c = "".join(["h", "i"])
print(id(a) == id(c))This prints False on CPython. The id values themselves change from run to run and from machine to machine, so only the comparison is worth reading.
Two lessons follow. First, is compares identities, not contents, so it reports False for a and c even though they hold the same letters. Second, you cannot rely on is returning True for literals either, because interning is an implementation detail. Use == and never think about interning when you write business logic.
Immutability is what makes sharing safe. If nothing can ever change the characters, every name that points at the object sees the same, correct text forever.
Immutability in practice
Immutability is a promise: the contents of a string object never change after it is created. Here is the program that proves it.
The output is:
inside: ADA
outside: ada
hello HELLO
one two
TypeError: 'str' object does not support item assignmentTrace the first part. The module creates the string "ada" and stores it in name. It passes the value to shout, which gives the same object the local name text. Inside shout, the line text = text.upper() builds a new string, "ADA", and points text at it. The function then prints ADA. When shout returns, its name text disappears. The object that name points at is still "ada", because nothing ever changed it. The second print proves it.
The second part shows the same idea without a function call. greeting.upper() returns a new string that is stored in loud. The original greeting still holds hello. A program that forgets to store the result of a method like this one is wrong, and Python does not warn you, because the call is legal. This is the most common mistake for beginners, so the next section gives it extra attention.
The third part is the one that surprises people. sentence = sentence + " two" looks like changing the string, but it does not. The + operator builds a new string containing one two, and the name sentence is pointed at it. The old "one" is still around until Python frees it. The name changed; the string did not.
The last part shows the rule directly. Python refuses word[0] = "b" with a TypeError. To get a different word, build a new string, for example "b" + word[1:].
Why strings are immutable
There are three practical reasons, and an interviewer may ask for them.
- Safety when sharing. If you pass a string to a function, that function cannot change the text under you. Your code can assume the value it stored yesterday is the value it has today.
- Dictionary keys. A
dictfinds an entry by computing a hash, a number that stands for the key. If the key could change, its hash would change and the entry could never be found again. Immutable strings can be used as dictionary keys and as members of aset. A list cannot, because lists can change. - Interning. Sharing one object among many names is only safe when nobody can edit it.
The cost is that building text piece by piece creates many temporary objects. That is the topic of the next lesson, which introduces "".join and the list-based way to build text in a loop.
Reading text from the user
A program often gets its text from the keyboard or from a file. The built-in function input() reads one line from the keyboard and returns it as a string. It removes the line break at the end, and it keeps any spaces inside the line.
Before you run the next program, type the input shown below. The program is also saved in the module as plain code, because it reads from the keyboard.
line = input()
vowels = 0
for ch in line:
if ch.lower() in "aeiou":
vowels += 1
print("Vowels:", vowels)Type this input when the program waits for it:
Programming is funThe output is:
Vowels: 5The loop visits every character in the line. For each character it calls ch.lower(), so A and a both count. The expression ch.lower() in "aeiou" is True when the character is one of the five letters. The in test on a string checks whether the one-character string is a piece of "aeiou". The letters that count are o, a and i in Programming (three), i in is (one) and u in fun (one). The total is 5.
Notice that ch.lower() does not change ch. It returns a lowercase copy of one character, and the line itself stays the same.
Two more facts about input(). It always returns a string, even when the user types digits, so int(input()) is needed to get a number. And if the user types nothing and presses Enter, input() returns the empty string, not None.
Worked example 3: cleaning user input
Text from a user is rarely tidy. People type extra spaces, mix capital letters and forget that a name may have one word or two. A common first step is to clean the text, then compare or split it. The next program does that for a name typed at a login prompt.
stored = "ada lovelace"
typed = input()
cleaned = typed.strip().lower()
print(f"[{cleaned}] length {len(cleaned)}")
if cleaned == stored:
print("Welcome back, Ada.")
else:
print("Name not recognised.")
print("First name:", cleaned[0:cleaned.find(" ")])
print("Initial:", cleaned[0])Type this input, with the spaces exactly as shown, two at the start and two at the end:
ADA LOVELACE The output is:
[ada lovelace] length 12
Welcome back, Ada.
First name: ada
Initial: aTrace the first two lines. The typed text is 16 characters long: two spaces, the 11 letters and the word gap, then two spaces. strip() removes the two spaces at each end and returns a new string of 12 characters. lower() is then called on that new string, and the result is stored in cleaned. Because the calls are chained, the second call works on the result of the first, and nothing is thrown away. The == test passes, because both strings hold ada lovelace.
The expression cleaned.find(" ") finds the first space at index 3. The slice cleaned[0:3] returns the characters at indexes 0, 1 and 2, which is ada. The cleaned[0] expression returns a, the first character.
Now type a single word:
bobThe output is:
[bob] length 3
Name not recognised.
First name: bo
Initial: bNo error appears, and the first name is wrong. The cause is the value -1. find(" ") returns -1 when there is no space, and the slice cleaned[0:-1] then means "from index 0 up to but not including the last character". Python reads -1 as a position counted from the end, so it returns bo. This is a silent bug, and it is harder to find than an exception. Python's slices never raise an error for out-of-range ends, so a wrong value can pass straight through. The fix is to check the result of find before you use it:
space = cleaned.find(" ")
first = cleaned if space == -1 else cleaned[:space]When there is no space, the whole text is the first name. The same care is needed for cleaned[0]. If the user had typed nothing, cleaned would be the empty string, and cleaned[0] would raise IndexError. A good rule is to ask, for each index you compute, which values can make it invalid, and then test those values before the call.
Characters are numbers
Each character has a number called its code point. The built-in ord returns the number for a character, and chr returns the character for a number. The letter a is 97, b is 98, and so on up to z at 122. Capital letters start at 65: A is 65 and Z is 90. You can subtract characters through their numbers and compare them, and Python shows the result as a number.
print(ord("a")) # 97
print(ord("d") - ord("a")) # 3
print(chr(ord("a") + 1)) # b
print("a" < "b") # TrueThe expression ord("d") - ord("a") gives 3, because 100 - 97 = 3. This trick turns a lowercase letter into a position from 0 to 25. Letter counting uses that position as a list index. The next program counts every letter in a line of text.
text = input()
count = [0] * 26
for ch in text:
if "a" <= ch <= "z":
count[ord(ch) - ord("a")] += 1
for i in range(26):
if count[i] > 0:
print(chr(ord("a") + i), count[i])Type this input:
banana bandanaThe output is:
a 6
b 2
d 1
n 4Here is the trace for the first few characters. The character b has code 98, so ord("b") - ord("a") is 1, and count[1] goes from 0 to 1. The character a has position 0, so count[0] goes up. The space is not between a and z, so the if skips it. At the end, count[0] holds 6 because banana has three as and bandana has three more. The last loop turns each position back into a letter with chr(ord("a") + i).
The line "a" <= ch <= "z" is a chained comparison. Python reads it as two comparisons joined by and, so you do not need to write it twice. The code [0] * 26 makes a list of 26 zeros. A list is covered in the next lesson.
Python has no char type, so the comparison ch == "a" is valid and means the same as ch == 'a'. The mistake is therefore different from Java's: a longer string compared to a character never matches, and Python does not report an error for it.
Exceptions and reading error messages
When something goes wrong, Python stops your program and prints a traceback. A traceback lists the lines of code that were running when the error happened, and it ends with the exception type and a message. The type tells you what kind of problem happened. The message gives the details. The traceback shows which line of your code caused it.
Three exceptions appear often with strings.
IndexErrorhappens when an index is outside the string, for examples[10]on a string of length 5.AttributeErrorhappens when you ask an object for a method or attribute it does not have, for examples.lengthor a method call onNone.TypeErrorhappens when an operation gets a value of the wrong type, for example"ab" + 1, or when you try to change a string in place.
The program below catches each of them so you can see the exact messages.
The output is:
index: string index out of range
slice: 'llo'
none: 'NoneType' object has no attribute 'upper'
int: invalid literal for int() with base 10: '12a'
concat: can only concatenate str (not "int") to str
attr: 'str' object has no attribute 'length'Read each message as a short report. string index out of range says the index you asked for does not exist. The message does not print the index or the length, so you must read the line of code to find them. The slice s[2:9] does not raise an error at all. It returns 'llo', because a slice stops at the end of the string. That is a difference from indexing that beginners often miss.
The AttributeError for None names the type, NoneType, and the method you tried to call. When you see it, look at the line in the traceback, find the variable on that line and check why it is None.
The ValueError from int names the text that could not be read. int("12a") fails because a is not a digit.
Uncaught exceptions look like this. The next program does not catch the error, so Python prints the traceback and stops. The file is main.py, and the path shown on your machine will differ.
word = "Hello"
print(word[10])The real output on Python 3.14 is this:
Traceback (most recent call last):
File "/home/example/main.py", line 2, in <module>
print(word[10])
~~~~^^^^
IndexError: string index out of rangeThe last line is the important one. It names the exception and its message. The line that starts with File gives the file and the line number. The caret lines under the code show which part of the line failed. Start reading at the bottom and work up to your own code.
Common mistakes
These are the mistakes that appear most often in beginner code. Each one has a fix you can apply in a few seconds once you recognise it.
Mistake 1: using is for text. The program below looks right, but it can print the wrong answer.
typed = "".join(["y", "e", "s"])
if typed is "yes":
print("matched")
else:
print("not matched")It prints not matched, because typed is a new object built at run time. Python also warns about this pattern with a SyntaxWarning when the literal is written directly. The fix is typed == "yes".
Mistake 2: forgetting that a method returns a new string. This program prints the original text, not the upper-case text:
name = "ada"
name.upper()
print(name) # prints adaThe fix is name = name.upper(), which stores the new string in the name.
Mistake 3: calling len as a method, or using a field name. The code s.len() raises an AttributeError, and so does s.length. Python shows these as runtime errors, not compile errors, because it checks them only when the line runs:
AttributeError: 'str' object has no attribute 'len'The fix is len(s). The built-in len works on strings, lists and most other containers.
Mistake 4: a slice that silently cuts text short. "abc"[1:4] returns "bc", not an error, because the slice stops at the end of the string. If you expected three characters, the result is wrong without any warning. Check the length after slicing when the input may be short. A plain index such as "abc"[4] does raise IndexError.
Mistake 5: a None value. t = None followed by t.upper() raises AttributeError: 'NoneType' object has no attribute 'upper'. Check for None before you use a value that might be missing, or make sure the value is always set.
Mistake 6: mixing numbers and strings. "Total: " + 5 raises TypeError: can only concatenate str (not "int") to str. The fix is "Total: " + str(5) or an f-string, f"Total: {5}".
Mistake 7: thinking an empty string is None. "" has length 0 and is a real string. None has no string at all. A check such as if not s: is true for both, which is useful when you only need a value to be present, but it hides the difference, so use s is None or not s on purpose.
Debugging walkthrough: a method that changes nothing
Here is a small function with a bug. It is meant to clean a user's typed name by removing leading and trailing spaces and making it lowercase, then check whether it is "ada".
def is_ada(raw):
raw.strip()
raw.lower()
return raw is "ada"
print(is_ada(" ADA "))The program prints False. A beginner usually expects True. Debug it the way a professional does: write down what each line should change, then check the actual value.
- Start with the input,
" ADA ". The length is 7, including the two spaces at each end. - Line one,
raw.strip(), should produce"ADA". Does it? No.strip()returns a new string, and the result is thrown away.rawstill holds" ADA ". - Line two,
raw.lower(), has the same problem. Its result is also thrown away. - Line three compares
rawwith"ada"usingis. Even ifrawheld"ada",iscompares objects, so it is not a safe test here either.
There are two bugs: the results of the method calls are never stored, and is is used for text. The corrected version is:
def is_ada(raw):
cleaned = raw.strip().lower()
return cleaned == "ada"
print(is_ada(" ADA "))This prints True. The method calls are chained, so each result feeds the next call, and the final value is stored in cleaned. The comparison uses ==. The function is also shorter, which usually means it is easier to check.
A habit that catches this bug early is to print a value after each step while you are debugging. A line such as print(f"[{cleaned}]") shows the text with brackets, so spaces at the ends are visible. Brackets are the simplest way to see a hidden space. Python's repr function does the same job with quotes and escapes: print(repr(" ADA ")) prints ' ADA '.
Variations and edge cases
Good string code works for the inputs you did not think about at first. Check these cases before you trust a function.
- The empty string.
len("")is 0.""[0]raisesIndexError, because there is no index 0. A loop withfor ch in s:never runs for an empty string, which is what you want. - Only spaces.
" ".isspace()isTrue." " == ""isFalse, because the string holds three characters. Choose the test that matches your rule, and use.strip() == ""when you want "blank counts as empty". - Case.
"Level" == "level"isFalse."Level".casefold() == "level".casefold()isTrue. Usecasefold()when you compare text that may hold letters from other languages, andlower()for everyday English. - Whitespace.
strip()removes spaces, tabs and line breaks at both ends. It also removes some characters that look like spaces but are not plain spaces, such as the non-breaking space. Usestrip()by default. - Missing text. A function that returns
Nonefor a missing value forces every caller to check. Decide early whether a value can beNone, and check it once at the boundary of your code. - Ranges that touch the end.
s[len(s):]is the empty string, and it is allowed.s[len(s) + 1:]is also the empty string, because slices are forgiving. A plain index atlen(s)raisesIndexError. - Searching for a missing piece.
findreturns -1 when the piece is not found. Test for -1 before you use the result as an index, or the next slice can return the wrong text, as thebobexample showed.
Each of these is a single line of code to check, and each one is a test case you should write for every string function you build.
Characters beyond plain English
So far every character has been a letter, a digit or a common punctuation mark. Python 3 handles every Unicode character directly. A string is a sequence of code points, one per character, and len counts code points. The emoji below is one code point, so its length is 1.
smile = "😀"
print(len(smile))
print(ord(smile))
print(hex(ord(smile)))
print(smile.encode("utf-8"))
print(len("é"), len("é"))The output is:
1
128512
0x1f600
b'\xf0\x9f\x98\x80'
1 2The last line shows that "é" as one code point has length 1, while "e" followed by a combining accent, written "é", has length 2. Both look the same on the screen. The encode method turns the text into bytes. UTF-8 is the usual byte format for files and web pages, and the emoji takes four bytes in it.
Other languages count differently. In Java, length() counts 16-bit units, so one emoji has length 2. Python counts code points, so the same emoji has length 1. Neither count is the number of symbols you see on the screen when a character is built from several code points, such as the accented e above. For full text handling of user-perceived characters, the standard library's unicodedata module and third-party libraries help, but the lessons in this module treat one code point as one character. Say that assumption out loud in an interview: "I assume each character is one code point, which holds for plain English text." That one sentence shows that you know where the simple model stops.
Interview view: what interviewers ask first
Interviewers do not usually ask you to define immutability. They ask questions that depend on it, and the answers reveal whether you understand the cost of each operation. The table below lists the costs you should be able to state for CPython.
| Operation | What it costs | Why |
|---|---|---|
len(s) | O(1) | The length is stored in the object |
s[i] | O(1) | Direct access to one position |
s == t | O(n) in the worst case | Compares characters one by one until a difference |
s[a:b] | O(b - a) | Copies the characters into a new string |
x in s and s.find(t) | O(n * m) in the worst case | Tries each start position against a pattern of length m |
s + t | O(len(s) + len(t)) | Builds a new string with a copy of both parts |
The cost of slicing surprises people, because it looks like a simple lookup. It is not. A slice copies the characters you ask for. A loop that slices a long string to read one character at a time does far more work than you expect. Read the characters with an index or with for ch in s instead.
A typical interview warm-up is this question: "Is one string a rearrangement of another?" The answer, which you will meet in the palindromes and anagrams lesson, uses a count of each letter. Before you write any code, say what you will compare, what it costs and what the edge cases are. That habit, more than any single algorithm, is what interviewers listen for.
Practice
Try each exercise before you open the solution. The first few are short; the last ones combine several ideas from this lesson.
- Print the last character of any string
swithout writing the index by hand. (Hint: negative indexes count from the end.) - What does
"banana".find("an")return, and what does"banana".rfind("an")return? Predict both, then check. - Count how many times the letter
aappears in"banana"using a loop. Then count how many times the piece"an"appears, usingfindin a loop, moving the start position forward after each match. Which result is 2 and why? - Write a function
is_blank_or_none(s)that returnsTruewhensisNone, empty, or contains only whitespace. - Given
word = "python", write one line that printsPYTHONwithout changingword. Then write one line that changeswordtoPYTHON. - Explain in two sentences why
s = "abc"followed bys + "def"and thenprint(s)still printsabc, and which line changess. - Write a function
middle(s)that returns the middle character of an odd-length string, and the two middle characters of an even-length string. Test it on"abcde","abcd"and"". For the empty string, decide what you want and document it. - Read one line with
input(). PrintYESif it contains the wordjavain any letter case, andNOotherwise. Lowercase the line once, then usein. - Write a program that reads a line and prints the position of each
e, one per line, using only indexes and a loop. - A colleague writes
if name.strip() is "":to check for a blank name. Give two separate reasons this is wrong, and write the correct condition.
Show the answer
print(s[-1])prints the last character. Negative indexes count from the end, so-1is the last position.s[len(s) - 1]gives the same result."banana".find("an")returns 1, the first position whereanbegins."banana".rfind("an")returns 3, the last position. The twoanpieces start at 1 and 3.- Using a loop over characters, the count of
ais 3. Usingfindin a loop, the pieceanappears at positions 1 and 3, so the count is 2. Each timefindreturns a position, the next search starts one character after it. The first count counts letters, the second counts pieces of two letters. def is_blank_or_none(s): return s is None or s.strip() == "". Thestrip()call removes all whitespace, so an empty string and a string of only spaces both become"". The explicit check forNoneis needed becauseNonehas nostripmethod.print(word.upper())printsPYTHONand leaveswordalone.word = word.upper()changes the name to point at the new upper-case string.s + "def"returns a new string,"abcdef", and the program throws it away. The namesstill points at"abc", because strings are immutable. To keep the result, writes = s + "def".- Let
n = len(s). Ifnis 0, return"", or raise an exception if you prefer, and say so in a comment. Ifnis odd, returns[n // 2]. Ifnis even, returns[n // 2 - 1 : n // 2 + 1]. For"abcde"the answer isc, for"abcd"it isbc. text = input().lower(), thenprint("YES" if "java" in text else "NO"). Lowercasing once makes the test case-blind.- Loop over
range(len(line)); ifline[i] == "e", printi. For"where are the eels"this prints 2, 4, 8, 12, 14 and 15, one per line. - Two reasons. First,
iscompares object identity, so the test can fail even when the stripped text is empty. Second, the code""in anistest is a literal, and interning makes the result depend on how the code was compiled. The correct test isname.strip() == "", ornot name.strip()whennameis neverNone.
What you now know
- A string is a row of characters with indexes from 0 to
len(s) - 1. Negative indexes count from the end. - The end index of
s[start:end]is exclusive, and the result hasend - startcharacters. Slices never raise an error for out-of-range ends. - Compare text with
==, neveris.iscompares object identity. - A string is immutable. Every method returns a new string, so you must store the result.
- Interning can make
isseem to work for literals. Do not depend on it. input()reads a whole line as a string, including spaces, and removes the line break.- A character is a string of length 1, and
ordandchrconvert between characters and code points.ord("d") - ord("a")gives a letter's position from 0 to 25. - The error messages for index and
Noneproblems name the type of the problem. Read the line number in the traceback, then check the values on that line.
The next lesson covers building text in a loop with "".join and the list-based approach, and the cost of building text by repeated +.