Everything about strings in one place โ creation, indexing & slicing, immutability, operators, and the full built-in method toolbox.
Table of Contents
Strings in Python are a sequence of Unicode characters. This part covers the different ways you can create a string in Python, the quirks around quote characters, and how to build multi-line strings.
SyntaxError.'''...''' or """...""") are also used for multi-line strings.str() function can also be used to create a string.Using single quotes
# Creating
c = 'Hello'
print(c)
Hello
Using double quotes
c = "Hello"
print(c)
Hello
Point to remember: Single and double quotes behave the same for simple strings โ the choice mostly matters when the string itself contains a quote character.
If you try to write a string containing an apostrophe using single quotes, Python gets confused about where the string ends:
'It's raining outside'
File "<ipython-input-16-7f9c68e857c8>", line 1
'It's raining outside'
^
SyntaxError: invalid syntax
Why this happens: Python reads the first ' as the start of the string and the ' in It's as the end of the string. Everything after that (s raining outside') becomes invalid code, causing a SyntaxError.
The fix โ use double quotes instead
c = "It's raining outside"
Because the string is now delimited by ", the apostrophe inside it is treated as a normal character, not a string terminator.
Tip: As a rule of thumb โ if your string contains an apostrophe ('), wrap it in double quotes"...". If it contains a double quote ("), wrap it in single quotes'...'.
Triple quotes can use either three single quotes or three double quotes:
c = '''Hello'''
print(c)
Hello
c = """Hello"""
print(c)
Hello
For a simple one-word string, triple quotes behave just like single/double quotes.
Triple quotes โ Multi-line strings
The real power of triple quotes is writing multi-line strings directly in the source code:
c = '''Hello'''
print(c)
# multi line strings
# multi line strings) while demonstrating that triple-quoted strings support multi-line text; the exact multi-line example content wasn't fully visible in the screenshots.Important points:
'''...''' and """...""" are functionally interchangeable.You can also explicitly create a string using the built-in str() function:
c = str("Hello")
c
Out: 'Hello'
Note: str() is more commonly used to convert other data types (numbers, lists, etc.) into strings, but it can also just wrap an existing string.
'Hello', "Hello", '''Hello''', """Hello""" โ all create the same string for simple text.SyntaxError: invalid syntax.''' or """) are used for multi-line strings and docstrings.str() can also be used to create/convert a string.Python lets you create strings with single quotes, double quotes, or triple quotes โ they're mostly interchangeable for simple text. The catch: if your string contains the same quote character used to wrap it (e.g., an apostrophe inside '...'), Python throws a SyntaxError because it thinks the string ended early. Fix this by switching to the other quote type. Triple quotes (''' or """) are the go-to choice for multi-line strings. You can also build a string using the str() function.
Since a string is a sequence of characters, each character has a position (index). This part covers:
Basic Indexing
# Concept of Indexing
c = "hello"
print(c)
hello
Every character in a string has a numeric position, starting from 0 for the first character.
print(c[4])
o
c[4] picks the character at index 4. For "hello" (h-e-l-l-o), index 4 is 'o'.
Out-of-range Index โ IndexError
print(c[5])
IndexError: string index out of range
Why: "hello" has 5 characters, so valid indices are 0 to 4. Index 5 doesn't exist.
โ ๏ธ Important: String indices are always 0-based. For a string of lengthn, valid positive indices go from0ton-1.
print(c[0])
h
c[0] always gives the first character of the string.
Types of Indexing
Python supports two ways to index a string:
| Type | Direction | Example (for "hello") |
|---|---|---|
| Positive Indexing | Counts from the start (left โ right), beginning at 0 | c[0] โ h, c[4] โ o |
| Negative Indexing | Counts from the end (right โ left), beginning at -1 | c[-1] โ o, c[-2] โ l |
Index map for "hello":
| Character | h | e | l | l | o |
|---|---|---|---|---|---|
| Positive index | 0 | 1 | 2 | 3 | 4 |
| Negative index | -5 | -4 | -3 | -2 | -1 |
# Types of Indexing
# Positive Indexing
# Negative Indexing
print(c[-2])
l
c[-2] accesses the 2nd character from the end โ 'l'.
print(c[-4])
l
c[-4] on "hello" should return 'e', but the screenshot's visible output looked like 'l'. Please re-verify this specific output by running the code yourself.Important points on indexing:
IndexError: string index out of range.Slicing lets you extract a substring using the syntax:
string[start : stop : step]
0.stop is not included). Default: end of string.1.Basic Slicing
# Slicing
c = "Hello World"
print(c)
Hello World
print(c[0:4])
Hell
Takes characters from index 0 up to (but not including) index 4.
print(c[0:5])
Hello
Omitting start or stop
print(c[2:])
llo World
Leaving stop empty means "go all the way to the end."
print(c[:4])
Hell
Leaving start empty means "start from the beginning."
print(c[:])
Hello World
Leaving out both start and stop returns the entire string โ this is a common way to make a copy of a string.
print(c)
Hello World
(Confirms c itself is unchanged โ slicing doesn't modify the original string, it just returns a new one.)
Slicing with a step
print(c[0:8:3])
HlW
With step = 3, Python picks characters at indices 0, 3, 6 โ H, l, W.
Note: In the video, the instructor first ran this slice and got a different/incomplete result before re-running it correctly to get HlW โ a good reminder to always double check your slice indices against the string's actual character positions.
Step direction matters
print(c[0:6:-1])
start to be greater than stop (moving right to left); here start=0 and stop=6 go the "wrong way" for a negative step, so no characters are returned.print(c[-5:-1:2])
Wr
Using negative indices as start/stop: -5 corresponds to 'W' and it steps forward by 2 up to (but excluding) index -1, picking 'W' and 'r'.
Reversing a String
print(c[::-1])
dlroW olleH
This is the classic Python trick to reverse a string. With start and stop omitted and step = -1, Python walks the entire string backward, one character at a time.
print(c[-1:-5:-1])
dlro
Starting at the last character (-1 = 'd') and stepping backward (step = -1) down to (but excluding) -5 ('W'), giving 'd', 'l', 'r', 'o'.
str[index] โ returns a single character; supports both positive (0, 1, 2, ...) and negative (-1, -2, -3, ...) indices.IndexError: string index out of range.str[start:stop:step] โ slicing syntax; stop is always exclusive.start โ defaults to the beginning; omit stop โ defaults to the end; omit both (str[:]) โ full copy of the string.str[::-1] is the go-to idiom for reversing a string.step requires start to come after stop in the string (i.e., moving right-to-left) โ otherwise you get an empty result.Indexing (c[i]) fetches a single character โ positive indices count from the left starting at 0, negative indices count from the right starting at -1; going out of range throws an IndexError. Slicing (c[start:stop:step]) extracts a substring: start is inclusive, stop is exclusive, and step controls the jump size and direction. Omitting parts of the slice gives sensible defaults (start of string, end of string, or the whole string with c[:]). The step can be negative to move backward โ and c[::-1] is the classic one-liner to reverse an entire string.
This part answers a key question: can you edit or delete characters inside a string directly? The short answer is no โ and the reason why is one of the most important properties of Python strings: they are immutable.
TypeError: 'str' object does not support item assignment.TypeError: 'str' object doesn't support item deletion.del, but not individual characters within it.del-ing a variable, referencing it again raises NameError: name '<var>' is not defined.c = "Hello"
print(c)
Hello
Trying to change the first character:
c[0] = 'X'
TypeError: 'str' object does not support item assignment
Why: Strings don't support item assignment. You cannot reach into a string and overwrite one of its characters the way you can with a list (my_list[0] = 'X' would work fine for a list).
# Strings are a Immutable Data Type
c = "world"
print(c)
world
Trying again with a different index:
c[5] = "X"
TypeError: 'str' object does not support item assignment
Note: It doesn't matter which index you try (0,5, etc.) โ no index of a string can ever be reassigned, because the string object itself is immutable.
If you need a "modified" string...
Since strings can't be edited in place, the standard approach is to create a new string โ e.g., using slicing/concatenation, or string methods (covered in Part 5) โ and assign it back to the variable if needed.
# Deletion
c
Out: 'world'
Trying to delete a single character:
del c[0]
TypeError: 'str' object doesn't support item deletion
Trying to delete a slice of characters:
del c[:3:2]
TypeError: 'str' object does not support item deletion
Why: Just like item assignment, item deletion isn't supported on strings โ you cannot remove a character (or a range of characters) from a string in place, for the same immutability reason.
While you can't delete part of a string, you can delete the variable that points to it entirely, using del:
del c
After this, the variable c no longer exists:
print(c)
NameError: name 'c' is not defined
Why: del c removes the variable binding itself from memory โ it's not modifying the string object, it's just deleting the name that referred to it.
str[i] = 'x' โ TypeError: 'str' object does not support item assignmentdel str[i] or del str[slice] โ TypeError: 'str' object doesn't support item deletiondel variable_name โ works fine; it deletes the variable, not the string's contents.del-ing a variable, using it again raises NameError: name '<var>' is not defined.Python strings are immutable: you cannot assign a new character to an index (c[0] = 'X' โ TypeError: item assignment), and you cannot delete a character or slice from a string (del c[0] โ TypeError: item deletion). The only thing you can delete is the variable name itself (del c), after which trying to use that variable raises a NameError. If you need a modified version of a string, you must always create a new string โ the original object can never be changed in place.
This part covers the different operators you can use directly on strings:
Strings support two arithmetic-style operators: + (concatenation) and * (repetition).
Concatenation (+)
"Hello" + "world"
Out: 'Helloworld'
Note: + simply glues the strings together character-for-character โ it does not insert a space automatically.
"Hello" + "-" + "world"
Out: 'Hello-world'
You can chain multiple + operators to join several strings (and any literal characters, like "-", in between).
Repetition (*)
print("*" * 50)
**************************************************
print("Hello" * 4)
HelloHelloHelloHello
Note: string * n repeats the entire string n times, back-to-back with no separator.
Strings can be compared with ==, !=, <, >, <=, >= โ just like numbers.
"Hello" == "World"
Out: False
"Hello" != "WOrld"
Out: True
Comparing with < and > โ Lexicographic (Dictionary) Order
"Mumbai" > "Pune"
# Lexiographically
Out: False
"Goa" < "Kolkata"
Out: True
How it works: String comparisons are done lexicographically (like dictionary/alphabetical order) โ Python compares characters one by one based on their underlying Unicode/ASCII values, until it finds a difference.
The Case-Sensitivity Trap
"kol" < "Kol"
Out: False
Why: String comparison is case-sensitive. In ASCII/Unicode, lowercase letters have higher numeric values than their uppercase counterparts ('k' > 'K'). So "kol" (starting with lowercase k) is actually considered greater than "Kol" (starting with uppercase K) โ which is why "kol" < "Kol" evaluates to False.
"apple" == "Apple" is False, and sorting/comparing mixed-case strings can give unintuitive results unless you normalize case first (e.g., with .lower()).Python's and, or, and not work on strings too, based on truthy/falsy values.
Truthy vs Falsy Strings
| String | Truthy/Falsy |
|---|---|
"" (empty string) | Falsy โ False |
Any non-empty string (e.g., "wreghr") | Truthy โ True |
not ""
Out: True
print(not "hello")
False
not simply flips the truthy/falsy value โ since "hello" is truthy, not "hello" is False; since "" is falsy, not "" is True.
and and or Return a Value, Not Just True/False
Unlike == or <, the and/or operators on strings return one of the operands themselves, not a plain boolean.
"hello" and "world"
Out: 'world'
Rule for and: If the first operand is truthy, Python evaluates and returns the second operand. (If the first operand were falsy, and would short-circuit and return the first operand instead.)
"" and "Hello"
Out: ''
Here, "" is falsy, so and short-circuits immediately and returns the first operand ("") without even looking at "Hello".
"" or "world"
Out: 'world'
Rule for or: If the first operand is falsy, Python moves on and returns the second operand.
"hello" or "world"
Out: 'hello'
Here, "hello" is truthy, so or short-circuits immediately and returns the first operand ("hello") without evaluating "world".
๐ก Quick mental model:
andโ returns the first falsy value, or the last value if all are truthy.orโ returns the first truthy value, or the last value if all are falsy.
Since a string is a sequence, you can iterate over it directly with a for loop โ each iteration gives you one character.
c = "hello world"
for i in c:
print(i)
h
e
l
l
o
w
o
r
l
d
Note: The loop prints every character, including the space between "hello" and "world" (shown above as a blank line).
Combining Loops with Slicing
You can loop over a slice of the string instead of the whole thing:
c = "hello world"
for i in c[2:7:2]:
print(i)
l
o
w
This loops only over the characters picked out by the slice c[2:7:2] (start=2, stop=7, step=2) โ i.e., indices 2, 4, 6 โ 'l', 'o', 'w'.
w, o, l, l, e, h), but the exact code that produced it wasn't visible. It appeared to demonstrate looping over a reversed or sliced version of the string, similar to the pattern above โ worth re-running examples like for i in c[::-1]: print(i) yourself to see reversed iteration in action.These check whether a character (or substring) exists inside a string, returning True or False.
'h' in c
Out: True
'H' in c
Out: False
Note: Just like relational operations, membership checks are case-sensitive โ 'h' and 'H' are treated as different characters.
'world' not in c
Out: False
Reading this carefully: 'world' not in c is False because 'world' is actually present in c โ not in returns True only when the item is absent.
'n' in c
Out: True
+ concatenates strings (no automatic spacing); * repeats a string n times.<, >, ==, etc.) compare strings lexicographically, based on character-by-character Unicode/ASCII values.and / or on strings don't return plain True/False โ they return one of the actual operands, based on truthy/falsy short-circuiting."" (empty string). Every non-empty string is truthy.for loop over a string yields its characters one at a time, in order (including whitespace); this can be combined with slicing (c[start:stop:step]).in / not in check whether a character or substring exists within a string โ case-sensitive.Strings support arithmetic-style operators: + concatenates and * repeats. Relational operators compare strings lexicographically using character ASCII values โ and remember, comparisons are case-sensitive (lowercase > uppercase in ASCII). Logical operators (and, or, not) work using truthy/falsy rules ("" is falsy, everything else is truthy) and โ unlike numeric comparisons โ and/or return one of the actual operand strings rather than a plain boolean. You can loop directly over a string's characters with for i in string:, optionally combined with slicing. Finally, in / not in let you check membership of a character or substring, again case-sensitively.
Python strings come with a large set of built-in methods (functions attached to the string object, called with string.method()). This part walks through the most commonly used ones: general-purpose functions, case conversion, counting, searching, checks, formatting, splitting, joining, replacing, and stripping whitespace.
These aren't string methods โ they're general Python functions that happen to work well on strings:
c = "kolkata"
len(c)
Out: 7
len() returns the number of characters in the string.
max(c)
Out: 't'
min(c)
Out: 'a'
max()/min() return the character with the highest/lowest Unicode value in the string.
sorted(c)
Out: ['a', 'a', 'k', 'k', 'l', 'o', 't']
sorted(c, reverse=True)
Out: ['t', 'o', 'l', 'k', 'k', 'a', 'a']
sorted() returns a list of the string's characters in sorted order (ascending by default; pass reverse=True for descending).
"it is raining today".capitalize()
Out: 'It is raining today'
.capitalize() uppercases only the first letter of the whole string, lowercasing the rest.
"it is raining today".title()
Out: 'It Is Raining Today'
.title() uppercases the first letter of every word.
c = "kolkata"
c.upper()
Out: 'KOLKATA'
c.upper().lower()
Out: 'kolkata'
.upper() / .lower() convert the entire string to upper/lower case. Methods can be chained โ .upper().lower() calls .lower() on the result of .upper().
"KoLkAtA".swapcase()
Out: 'kOlKaTa'
.swapcase() flips the case of every character โ upper becomes lower and vice versa.
"it is raining".count("x")
Out: 0
"it is raining".count("i")
Out: 4
"it is raining".count("is")
Out: 1
"it is raining".count("ing")
Out: 1
.count(sub) returns how many non-overlapping times sub appears in the string.
"it is raining".find("x")
Out: -1
"it is raining".find("g")
Out: 12
"it is raining".index("x")
ValueError: substring not found
"it is raining".index("rain")
Out: 6
Key difference between .find() and .index():
| Method | Substring found | Substring NOT found |
|---|---|---|
.find(sub) | Returns the starting index | Returns -1 |
.index(sub) | Returns the starting index | Raises ValueError: substring not found |
๐ก Use.find()when a missing substring is a normal case you want to handle gracefully; use.index()when you'd rather the program fail loudly if the substring is expected to be there.
"it is raining".endswith("ing")
Out: True
"it is raining".endswith("ingef")
Out: False
"it is raining".startswith("it")
Out: True
These check whether the string ends with or begins with a given substring, returning True/False.
.format() lets you build strings with placeholders ({}) that get filled in with values.
"Hello my name is {} and I am {}".format("Nitish", 30)
Out: 'Hello my name is Nitish and I am 30'
Empty {} placeholders are filled in order by the arguments passed to .format().
"Hello my name is {1} and I am {0}".format("Nitish", 30)
Out: 'Hello my name is 30 and I am Nitish'
You can put a positional index inside {} to control which argument fills which placeholder โ {1} refers to the 2nd argument (30), {0} to the 1st ("Nitish").
"Hello my name is {age} and I am {name}".format(name="Nitish", age=30)
Out: 'Hello my name is 30 and I am Nitish'
You can also use named placeholders matched to keyword arguments โ note the placeholder name doesn't have to "make sense" relative to the value; Python just matches the placeholder name to the keyword argument name.
"Hello my name is {age} and I am {weight}".format(name="Nitish", age=30, weight=70)
Out: 'Hello my name is 30 and I am 70'
Extra keyword arguments that aren't referenced by any {} placeholder (like name here) are simply ignored.
These all return True/False checks about what kind of characters a string contains.
"FLAT20&".isalnum()
Out: False
.isalnum() checks if all characters are letters or digits โ fails here because of &.
"FLAT20".isalpha()
Out: False
.isalpha() checks if all characters are letters โ fails here because of the digits 2, 0.
"20A".isdigit()
Out: False
.isdigit() checks if all characters are digits โ fails here because of the letter A.
"hello world".isidentifier()
Out: False
"hello_world".isidentifier()
Out: True
.isidentifier() checks whether the string would be a valid Python variable name โ spaces aren't allowed (so "hello world" fails), but underscores are fine (so "hello_world" passes).
๐กisdecimal()is also part of this family (not shown running here) โ it's similar toisdigit()but stricter about which numeric characters count as "decimal."
"who is the pm of india".split()
Out: ['who', 'is', 'the', 'pm', 'of', 'india']
With no argument, .split() breaks the string into a list of words, splitting on any whitespace.
"who is the pm of india".split("i")
Out: ['who ', 's the pm of ', 'nd', 'a']
Passing a separator splits on every occurrence of that exact substring, removing it from the result.
"who is the pm of india".split("x")
Out: ['who is the pm of india']
If the separator doesn't appear anywhere in the string, .split() returns a list containing the entire original string as its only element.
.join() is the reverse of .split() โ it combines a list of strings into one string, using the string it's called on as the separator.
" ".join(['who', 'is', 'the', 'pm', 'of', 'india'])
Out: 'who is the pm of india'
"/".join(['who', 'is', 'the', 'pm', 'of', 'india'])
Out: 'who/is/the/pm/of/india'
"-".join(['who', 'is', 'the', 'pm', 'of', 'india'])
Out: 'who-is-the-pm-of-india'
Whatever string .join() is called on becomes the glue placed between every element of the list.
"Hi my name is Nitish".replace("Nitish", "Amit")
Out: 'Hi my name is Amit'
.replace(old, new) returns a new string with every occurrence of old swapped for new.
name = " nitish "
name.strip()
Out: 'nitish'
.strip() removes leading and trailing whitespace (spaces, tabs, newlines) from a string โ it does not touch whitespace in the middle.
๐ก Related methods (seen via autocomplete, not individually demonstrated):.lstrip()strips only from the left,.rstrip()strips only from the right. Other string methods available include.partition(),.rpartition(),.rfind(),.rindex(),.rjust(),.rsplit(), and.maketrans()โ worth exploring in the Python docs or via tab-autocomplete in a notebook.
.capitalize() (first letter of string), .title() (first letter of each word), .upper(), .lower(), .swapcase() โ all return a new string (strings are immutable)..count(sub) counts non-overlapping occurrences; .find(sub) returns -1 if missing; .index(sub) raises ValueError if missing..startswith() / .endswith() check prefixes/suffixes and return booleans..format() fills {} placeholders โ positionally, by index ({0}, {1}), or by keyword name ({name}).is...() family (isalnum, isalpha, isdigit, isidentifier, etc.) checks whether all characters in the string satisfy a condition..split(sep) breaks a string into a list (default: split on whitespace); .join(list) does the reverse, gluing a list back into a string..replace(old, new) swaps every occurrence of a substring..strip() removes leading/trailing whitespace only; use .lstrip()/.rstrip() for one-sided stripping.Strings have a rich set of built-in methods. For case handling: .capitalize(), .title(), .upper(), .lower(), .swapcase(). For searching: .count() counts matches, .find() returns -1 if not found while .index() raises an error, and .startswith()/.endswith() check prefixes/suffixes. .format() builds strings from placeholders filled positionally, by index, or by keyword. The is...() methods (isalnum, isalpha, isdigit, isidentifier, etc.) validate what kind of characters a string contains. .split() turns a string into a list of pieces; .join() does the reverse. .replace() swaps substrings, and .strip() (plus .lstrip()/.rstrip()) trims whitespace from the ends.