String Type Fundamentals

NoteWhat is a string?

A Python string is a positionally ordered collection of other objects. Sequences maintain a left-to-right order among the items they contain: their items are stored and fetched by their relative positions. Strictly speaking, strings are immutable sequences of one-character strings; other, more general sequence types include lists and tuples, covered later.

NoteHow do I use strings?

Strings are used to record words, contents of text files loaded into memory, Internet addresses, Python source code, and so on. Strings can also hold the raw bytes used for media files and network transfers and the encoded and decoded forms of non-ASCII Unicode text used in internationalized programs.

NoteIs abc a Python string?

Nope. Python strings are enclosed in single quotes (‘…’) or double quotes (“…”) with the same result. Hence, “abc” can be Python string, while abc cannot. abc can be a variable name, though.

NoteHow do I manipulate string objects?

The fact that strings are immutable sequences affects how we manipulate textual data in Python. In Snippet 4.6, we fetch the individual elements of S, a variable assigned to “Python 3.X.” As per the built-in function len, S contains ten unitary strings. That means that each element in S is associated with a position in the numerical progression {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}.

Now, you may be surprised that the list’s first element is 0 instead of 1. The reason is that Python is a zero-based indexed programming language: the first element of a series has an index of 0, while the last part has an index len(obj) - 1.

Fetching the individual elements of a string, such as S, requires passing the desired index between brackets, as shown in line 9 (where we get the first unitary string, namely, “P”.), line 13 (where we get the last unitary string, namely, “X”), and line 21 (where we get the unitary string with index 3, i.e., the fourth unitary string appearing in S, “h”). Note that line 17 is an alternative indexing strategy to the one presented in line 13: it is possible to retrieve the last unitary string by counting ‘backward’; that is, getting the first element starting from the right-hand side of the string, which equates to index -1.

In lines 26 and 31, we exploit the indices of S to retrieve multiple unitary strings in a row. What we pass among brackets is not a single index. Instead, we specify a range of indices i:j. It is worth noticing that, in Python, the element associated with the lower bound index i is returned. In contrast, the element associated with the upper bound index j is not. In line 26, we fetch the unitary strings between index 2 — equating to third unitary string of S — and index 5 excluded — namely, the fifth unitary string of S. In line 30, we adopt the ‘backward’ approach to retrieve the unitary string with index -3 — the third string counting from the right-hand side of S — as well as any other unitary strings following index -3. To do that, we leave the upper bound index blank.

# let us assign the string "Python 3.X" to the variable S
>>> S = "Python 3.X"

# check the length of S
>>> len(S)
10

# access the first unitary string in the sequence behind S
>>> S[0]
"P"

# access the last unitary string in the sequence behind S
>>> S[len(S)-1]
"X"

# or, equivalently
>>> S[-1]
"X"

# access the i-th, e.g., 3rd, unitary string in the sequence behind S
>>> S[3]
"h"

# access the unitary strings between the i-th and j-th positions in the
# sequence behind S
>>> S[2:5]
"tho"

# access the unitary strings following the i-th position in the sequence
# behind S
>>> S[-3:]
"3.X"
NoteWhat are the most common string literals and operators?

Snippet 4.6 deals with string indexing and slicing, two of the many operations we can carry out on strings. Table 4.5 reports a sample of common string literals and operators.

The first two lines of Table 4.5 remind us that single and double quotes are equivalent when assigning a variable to a string object. However, we must refrain from mixing and matching single and double quotes. In other words, a string object requires the leading and trailing quotes are of the same type (i.e., double-double or single-single).

In the interest of consistency, it is a good idea to make a policy choice, such as “in my Python code, I use double quotes only”, and to stick with that throughout the various lines of the script. I prefer using double quotes because the single quote symbol is relatively popular in natural language (consider, for example, the Saxon genitive).

As shown in the third line of Table 4.5, the single quote is treated as a unitary string insofar as double quotes are used to delimit the string object. Should the string object be delimited by single quotes, we should tell Python not to treat the single quote symbol after m as a Python special character but as a unitary string. To do that, we use the escape symbol \ as shown in the fourth line of Table 4.5.

Table 1: Sample of String Literals and Operators
Literal/operation Interpretation
S = "" Empty string
S = '' Single quotes, same as double quotes
S = "spam's" Single quote as a string
S = 'spam\'s' Escape symbol
length(S) Length
S[i] Index
S[i:j] Slice
S1 + S2 Concatenate
S * 3 Repeat S n times (e.g., three times)
"text".join(strlist) Join multiple strings on a character (e.g., “text”)
"{}".format() String formatting expression
S.strip() Remove white spaces
S.replace("pa", "xx") Replacement
S.split(",") Split on a character (e.g., “,”)
S.lower() Case conversion — to lower case
S.upper() Case conversion — to upper case
S.find("text") Search substring (e.g., “text”)
S.isdigit() Test if the string is a digit
S.endswith("spam") End test
S.startswith("spam") Start test
S = """...multiline...""" Triple-quoted block strings
NoteCan you share some concrete examples of string manipulation tasks?

Snippet 4.7 presents a sample of ‘common’ miscellaneous string manipulation tasks. In lines 6 and 9, we check the length of the variables S1 and S2. In line 13, we display five repetitions of S1. In line 17, we use the algebraic operator “+” to concatenate S1 and S2. In line 21, we expand on the previous input by separating S1 and S2 by a white-space. In line 25, we carry out the same task as line 17 — however, we rely on the built-in join function to join S1 and S2 with whitespace. The argument taken by join is a Python list, the subject of paragraph 5.3. In line 29, S1 and S2 are joined with a custom string object, namely, " Vs. ". Finally, in line 33, we use the built-in format function (see also Snippet 4.4) to display a string object including S1 and S2.

# let us assign S1 and S2 to two strings
>>> S1 = "Python 3.X"
>>> S2 = "Julia"

# check the length of S1 and S2
>>> len(S1)
10

>>> len(S2)
5

# display the S1 repeated five times
>>> S1 * 5
"Python 3.XPython 3.XPython 3.XPython 3.XPython 3.X"

# display the concatenation of S1 and S2
>>> S1 + S2
"Python 3.XJulia"

# display the concatenation of S1, whitespace, and S2
>>> S1 + " " + S2
"Python 3.X Julia"

# display the outcome of joining S1 and S2 with a whitespace
>>> " ".join([S1, S2])
"Python 3.X Julia"

# display the outcome of joining S1 and S2 with an arbitrary string object
>>> " Vs. ".join([S1, S2])
"Python 3.X Vs. Julia"

# string formatting
>>> "Both {} and {} have outstanding ML modules".format(S1, S2)
"Both Python 3.X and Julia have outstanding ML modules"
NoteCan you share some concrete examples of string editing tasks?

Snippet 4.8 illustrates some string editing tasks. In line 5, we use lstrip — a variation of the built-in function strip — that returns a copy of the string with leading characters removed. Take care here: lstrip takes a set of characters, not a prefix. It removes any leading character that appears in the argument, so "Both " means the set {B, o, t, h, space}. It gives the expected answer below by luck; "tooth".lstrip("Both ") returns the empty string, because every one of its characters is in that set. When you mean ‘remove this prefix’, use removeprefix instead. In line 9, we use the built-in replace to return a copy of the string with all occurrences of substring old (first argument taken by the function) replaced by new (second argument taken by the function). Finally, in line 17, we use the built-in function lower to return a copy of the string with all the cased characters converted to lowercase.

# let us assign S to a string object
>>> S = "Both Python 3.X and Julia have outstanding ML modules"

# strip target leading characters
>>> S.lstrip("Both ")
"Python 3.X and Julia have outstanding ML modules"

# careful: lstrip takes a SET of characters, not a prefix
>>> "tooth".lstrip("Both ")
""

# to remove a prefix, say so
>>> S.removeprefix("Both ")
"Python 3.X and Julia have outstanding ML modules"

# replace target characters
>>> S.replace("Python 3.X", "R")
"Both R and Julia have outstanding ML modules"

# split string on target characters
>>> S.split(" and ")
["Both Python 3.X", "Julia have outstanding ML modules"]

# make the string lower case
>>> S.lower()
"both python 3.x and julia have outstanding ml modules"
NoteHow do I test or search string attributes?

Snippet 4.9 presents a series of string test and search tasks. The built-in function find (see lines 5 and 9) returns the lowest index in the string where substring sub is found within the slice S[start:end] or -1 if substring is not found. The built-in function isdigit return True if all characters in the string are digits and there is at least one character, False otherwise. Finally, the built-in function endswith returns True if the string ends with the specified suffix, otherwise returns False.

# let us assign S to a string object
>>> S = "The first version of Python was released in 1991"

# search for "Python" in S
>>> S.find("Python")
21

# search for "Julia" in S
>>> S.find("Julia")
-1

# slice the string the get Python's release year information
>>> SS = S[-4:]

# display SS
>>> SS
"1991"

# test if all characters in SS are digits
>>> SS.isdigit()
True

# test if S ends with "1991" / SS
>>> S.endswith(SS)
True
NoteCan I display ‘complex’ string objects?

We just came across the built-in function print. Such a function can print both number- and string-type objects. Sometimes, what we want to print fits into a single line. In other circumstances, we are interested in visualizing rich data which can span multiple lines. Snippet 4.9 how to print objects across multiple lines with the triple-quoted block string (see line ). As evident from the Python code in lines 8-13, any line between triple quotes is considered part of the same string object.

# single-line print
>>> print("Hello world!")
Hello world!

# multi-line print
>>> print(
... """
... =======================================================
... COL A       | COL B      | ...        | COL K
... -------------------------------------------------------
... Sheldon     | Cooper     | ...        | bazinga.com
... -------------------------------------------------------
... NOTES: this table has fake data
... """
... )

=======================================================
COL A       | COL B      | ...        | COL K
-------------------------------------------------------
Sheldon     | Cooper     | ...        | bazinga.com
-------------------------------------------------------
NOTES: this table has fake data