Chapter 6: Strings
Introduces the chapter’s focus on handling and formatting text in Python.
After this chapter you will be able to
- String indexing slicing and length. Explain that strings can be treated as sequences for extracting characters or substrings with indexes and slices, and that len gives the number of characters.
- String immutability and slicing. Distinguish strings from lists in terms of modification, and explain why slicing off a newline does not change the original string.
- Basic string operators. Explain that + concatenates Python strings and that Python also has a string multiplication operator, which the author finds useful less often.
- String escape sequences. Explain what an escape sequence is in a Python string and why it is used, giving newline and tab as examples.
- Two-character escape sequences. Explain that Python provides a short list of two-character escape sequences for strings and that the same sequences also apply to bytes objects.
- Numeric escapes for ASCII. Explain how octal and hexadecimal escape sequences put ASCII characters into strings, including characters with no printable representation.
- Unicode characters by number or name. Describe how Unicode escapes let a Python 3 string contain characters specified by their Unicode number or name.
- Evaluated versus printed strings. Explain why evaluating a string interactively can look different from printing that same string when it contains special characters.
- String Method Calls and Results. Explain that most string methods are available on string objects and are called with dot notation, such as x.upper(). Describe why a method’s return value must be used rather than expecting it to change the original string.
- Splitting and joining strings. Explain what split and join return, how whitespace or a specified delimiter affects split, and how the string used for join appears between elements. Apply both methods to replace whitespace with dashes.
- Numeric string conversion. Explain that int converts a string to an integer and float converts a string to a floating-point number, and that either raises ValueError if the string cannot be interpreted as the requested type.
- Whitespace trimming by string end. Explain what strip, lstrip, and rstrip remove by default, and why rstrip is useful for a line read from a file.
- Specified trimming characters. Explain how passing a string of characters changes what strip, lstrip, or rstrip removes.
- Substring positions and search bounds. Explain how find locates a substring, what it returns when there is no match, and how its optional start and end positions limit the search.
- Reverse matches and failure behavior. Distinguish rfind from find and index and rindex from their corresponding find methods, especially when no substring is found.
- Occurrence counts and edge checks. Describe what count returns and how startswith and endswith check a string’s beginning or end, including their use with a tuple of candidate strings.
- Immutable string replacement. Explain why modifying a string produces a new string, and describe how replace substitutes occurrences of a substring.
- Character translation and removal. Describe how maketrans and translate work together to change multiple individual characters, and how the optional removal argument is used.
- List-based string modification. Explain how to modify a string as though it were a list of characters: turn it into a list, change the list, and join the characters into a new string.
- String characteristic methods. Explain that string objects have methods for reporting characteristics such as whether their characters are digits or alphabetic, or whether they are all uppercase or lowercase.
- String module character constants. Describe the character collections supplied by the string module and explain how the in operator can test whether a character belongs to one of them.
- Object conversion for concatenation. Explain why an object must be converted to a string before it can be concatenated with other strings, and how repr can provide that string representation.
- Formal and printable representations. Distinguish the intended results of repr and str, including why they usually look the same for built-in objects discussed here and why str is a good choice for display.
- Format fields and literal braces. Explain how the format method combines a format string with replacement values, and how to include literal braces in the result.
- Numbered positional replacement fields. Explain how the string format method fills numbered replacement fields from positional parameters, regardless of where the fields appear in the format string.
- Named format replacement fields. Explain that format chooses a value for a named replacement field by matching the field’s name to a named parameter. Describe how a call can also use positional parameters and access an element of a named parameter, as in user[1].
- Replacement field format specifiers. Explain how format specifiers control a replacement field’s width, alignment, and padding, using the section’s examples.
- F-string expression interpolation. Explain how to write an f-string and how expressions inside it become part of the resulting string.
- Percent Formatting Substitution. Explain how the string modulus operator uses formatting sequences in a string on the left and values in a tuple on the right to produce a new string.
- Percent formatting sequences. Explain where formatting sequences appear in a percent-formatted string and how they specify substitutions. Describe how a numeric sequence can control field width, decimal places, and justification.
- Named percent-format substitutions. Explain how a %-formatting sequence names a value and how the dictionary on the right of % supplies that value. Describe why this is useful when a format string has many substitutions.
- Strings and bytes distinction. Distinguish a string, an immutable sequence of Unicode characters, from a bytes object, a sequence of integers. Explain why bytes are useful for binary data while strings represent text, including text with accented characters.
- Encoding and decoding bytes. Explain that a string’s encode method converts it to bytes and a bytes object’s decode method converts it back to a string. State that bytes and strings cannot be added together directly because they are incompatible types.
- Text cleaning workflow. Describe how to turn the source text into cleaned output: make it one case, remove punctuation, split it into words, and write one word per line to a second file.
- AI text preprocessing prompt. Describe the requirements to give an AI tool for preprocessing moby_01.txt, and state that the generated code should be tested.
What it builds on
- List indices (chapter 5) · Applies index notation previously used with lists.
- Modifying lists (chapter 5) · Contrasts strings with lists, which can be modified.
- Strings (chapter 4) · The earlier introduction to Python strings.
- Lists are like arrays (chapter 5) · A list can hold the substrings returned by split.
Section by section
Introduction
Introduces the chapter’s focus on handling and formatting text in Python.
6.1 Strings as sequences of characters
This section explains how to extract and count characters in strings while distinguishing strings from mutable lists.
- String indexing slicing and length: Explain that strings can be treated as sequences for extracting characters or substrings with indexes and slices, and that len gives the number of characters.
Why it matters: These familiar sequence operations let you work with parts of text and measure its length. - Single character string values: State what type of value results when a single character is extracted from a Python string.
Why it matters: Extracting one character does not require learning a separate Python character type. - String immutability and slicing: Distinguish strings from lists in terms of modification, and explain why slicing off a newline does not change the original string.
Why it matters: Recognizing that strings cannot be changed in place prevents list-style modification errors.
Key terms: index, slice notation, len, character, one-character string, empty string, strings, lists, slice, modified
6.2 Basic string operations
This section introduces the operators for combining strings and notes that string multiplication is less commonly useful.
- Basic string operators: Explain that + concatenates Python strings and that Python also has a string multiplication operator, which the author finds useful less often.
Why it matters: These are the basic operators the section introduces for working with strings.
Key terms: string concatenation operator +, string multiplication operator
6.3 Special characters and escape sequences
Introduces Python string escape sequences as backslash-starting sequences used to represent characters such as newline and tab.
- String escape sequences: Explain what an escape sequence is in a Python string and why it is used, giving newline and tab as examples.
Why it matters: This gives you a way to recognize characters such as tabs and newlines when they appear in string literals.
Key terms: escape sequences, special characters, backslash, newline character, tab character
6.3.1 Basic escape sequences
This subsection introduces Python’s two-character escape sequences and notes that they apply to both strings and bytes objects.
- Two-character escape sequences: Explain that Python provides a short list of two-character escape sequences for strings and that the same sequences also apply to bytes objects.
Why it matters: This helps you recognize that the same escape notation appears in both kinds of literal.
Key terms: two-character escape sequences, strings, bytes objects
6.3.2 Numeric (octal and hexadecimal) and Unicode escape sequences
This subsection explains how numeric escapes represent ASCII characters and how Unicode escapes represent characters by number or name.
- Numeric escapes for ASCII: Explain how octal and hexadecimal escape sequences put ASCII characters into strings, including characters with no printable representation.
Why it matters: Numeric escapes let you represent a character even when typing or displaying it directly is inconvenient. - Unicode characters by number or name: Describe how Unicode escapes let a Python 3 string contain characters specified by their Unicode number or name.
Why it matters: Unicode escapes provide ways to specify characters beyond the ASCII examples.
Key terms: ASCII, octal escape sequence, hexadecimal escape sequence, Unicode strings, Unicode names, \N, \u
6.3.3 Printing vs. evaluating strings with special characters
This subsection explains why evaluating and printing a string with special characters look different, and how to prevent print from adding a newline.
- Evaluated versus printed strings: Explain why evaluating a string interactively can look different from printing that same string when it contains special characters.
Why it matters: It helps you distinguish the contents of a string from how those contents appear on screen. - Print’s final newline: Describe the newline that print adds by default and how to suppress it when the string already ends with one.
Why it matters: It helps avoid an extra line break when printing text that already ends with one.
Key terms: evaluating, print function, special characters, newline, tab, end parameter, final newline
6.4 String methods
This section introduces how to call Python string methods and why their return values matter for immutable strings.
- String Method Calls and Results: Explain that most string methods are available on string objects and are called with dot notation, such as x.upper(). Describe why a method’s return value must be used rather than expecting it to change the original string.
Why it matters: It helps you use a string method’s result without mistakenly expecting the original string to change.
Key terms: string methods, string object, dot (.), return value, immutable
6.4.1 The split and join string methods
Explains how split and join turn strings into lists of substrings and back, how to choose delimiters, and why join is preferable to repeated concatenation.
- Splitting and joining strings: Explain what split and join return, how whitespace or a specified delimiter affects split, and how the string used for join appears between elements. Apply both methods to replace whitespace with dashes.
Why it matters: These methods make it easy to take text apart and put it back together with the separators you want. - Join versus repeated concatenation: Justify using join rather than repeated + when combining many strings.
Why it matters: The choice matters when combining large numbers of strings. - Limiting splits and preserving fields: Explain how the optional second argument limits the number of splits, why that can preserve arbitrary text in a final field, and how to request whitespace splitting when using that argument.
Why it matters: A split limit lets you parse initial fields without breaking apart the rest of the text.
Key terms: split, join, delimiter, whitespace, string concatenation, +, splits, None
6.4.2 Converting strings to numbers
This section explains how to convert numeric strings with int and float, including how invalid input and a specified base affect conversion.
- Numeric string conversion: Explain that int converts a string to an integer and float converts a string to a floating-point number, and that either raises ValueError if the string cannot be interpreted as the requested type.
Why it matters: Conversion lets you use text as a number while recognizing when the text is not valid numeric input. - Integer conversion base: Describe how int can take an optional second argument specifying the base used to interpret a string, and explain why a string containing the digit 6 fails when interpreted as base 6.
Why it matters: The chosen base determines which digits are valid in a string passed to int.
Key terms: int, float, ValueError, numeric base, base 6
6.4.3 Getting rid of extra whitespace
This subsection explains how to trim whitespace or specified characters from string ends, and how exact prefix and suffix removal differs.
- Whitespace trimming by string end: Explain what strip, lstrip, and rstrip remove by default, and why rstrip is useful for a line read from a file.
Why it matters: Choosing the right method lets you clean an input string without removing whitespace from the wrong end. - Specified trimming characters: Explain how passing a string of characters changes what strip, lstrip, or rstrip removes.
Why it matters: The argument is a set of characters to trim, not a literal piece of text to remove. - Exact prefix and suffix removal: Distinguish removeprefix and removesuffix from strip when removing text from a string's ends.
Why it matters: An exact-match method is more predictable when you want to remove a particular prefix or suffix.
Key terms: strip, lstrip, rstrip, whitespace, trailing newline, extra parameter, removeprefix, removesuffix, exact matches
6.4.4 String searching
This section explains Python’s simple string-searching methods and when a regular-expression search may be more suitable.
- Substring positions and search bounds: Explain how find locates a substring, what it returns when there is no match, and how its optional start and end positions limit the search.
Why it matters: These return values and bounds let you interpret a search result correctly. - Reverse matches and failure behavior: Distinguish rfind from find and index and rindex from their corresponding find methods, especially when no substring is found.
Why it matters: Choosing among these methods depends on which match you want and what should happen when there is none. - Occurrence counts and edge checks: Describe what count returns and how startswith and endswith check a string’s beginning or end, including their use with a tuple of candidate strings.
Why it matters: These methods answer common search questions directly without requiring you to interpret a match position. - Simple searches versus patterns: Distinguish a basic substring search from a search using the re module, and explain why a pattern search might be preferable.
Why it matters: Recognizing the limit of a literal substring search helps you choose a suitable search tool.
Key terms: find, substring, start, end, rfind, index, rindex, ValueError, count, nonoverlapping
6.4.5 Modifying strings
This section shows how to produce modified strings with replacement, character translation, removal, and other string operations without changing the original string.
- Immutable string replacement: Explain why modifying a string produces a new string, and describe how replace substitutes occurrences of a substring.
Why it matters: It helps you reason about a replacement’s result without expecting the original string to change. - Character translation and removal: Describe how maketrans and translate work together to change multiple individual characters, and how the optional removal argument is used.
Why it matters: A translation table handles several character substitutions together instead of requiring repeated calls to replace. - Other string transformations: Describe the kinds of changes the section lists for case conversion, tab expansion, padding, and zero-filling.
Why it matters: These named operations point to common text changes without requiring manual character-by-character work.
Key terms: immutable, replace, substring, newstring, maketrans, translate, translation table, lower, upper, capitalize
6.4.6 Modifying strings with list manipulations
This section shows how to manipulate a string through a list of characters and explains when that approach may be costly.
- List-based string modification: Explain how to modify a string as though it were a list of characters: turn it into a list, change the list, and join the characters into a new string.
Why it matters: It gives you a way to make character-by-character changes without trying to change an immutable string in place. - Cost of repeated conversions: Explain why the list-based method should not be overused and distinguish the section’s examples of workloads where its cost may or may not matter.
Why it matters: The technique is useful, but its cost can become noticeable when applied to very large numbers of strings.
Key terms: immutable objects, list of characters, "".join(), new string objects, relatively expensive
6.4.7 Useful methods and constants
This subsection introduces string methods for checking characteristics, useful constants in the string module, and the fact that the string operations discussed do not change the original string.
- String characteristic methods: Explain that string objects have methods for reporting characteristics such as whether their characters are digits or alphabetic, or whether they are all uppercase or lowercase.
Why it matters: These methods provide a direct way to check what kind of text a string contains. - String module character constants: Describe the character collections supplied by the string module and explain how the in operator can test whether a character belongs to one of them.
Why it matters: These constants give you ready-made character sets when a membership check is useful. - Original strings after operations: State that the string operations noted here do not change the original string and instead return a location or a string.
Why it matters: Remembering to use a returned result prevents you from expecting the original string to change.
Key terms: string methods, digits, alphabetic characters, uppercase, lowercase, string module, string.whitespace, string.digits, string.hexdigits, string.octdigits
6.5 Converting objects to strings
This section explains how repr and str turn Python objects into strings, why explicit conversion matters for concatenation, and which representation to use for debugging or display.
- Object conversion for concatenation: Explain why an object must be converted to a string before it can be concatenated with other strings, and how repr can provide that string representation.
Why it matters: Explicit conversion makes string concatenation work and gives you a way to inspect values while debugging. - Formal and printable representations: Distinguish the intended results of repr and str, including why they usually look the same for built-in objects discussed here and why str is a good choice for display.
Why it matters: The distinction helps you choose a representation suited to inspection or to display, even when both functions currently give you the same result.
Key terms: repr, string representation, string concatenation, str, formal string representation, informal string representation
6.6 Using the format method
Introduces Python’s string format method, explaining replacement fields and how to include literal braces.
- Format fields and literal braces: Explain how the format method combines a format string with replacement values, and how to include literal braces in the result.
Why it matters: This distinction lets you tell a value placeholder from a brace meant to appear in the output.
Key terms: format method, format string, replacement fields, replacement values
6.6.1 The format method and positional parameters
This subsection explains how numbered replacement fields in a format string use positional arguments and how doubled braces remain literal.
- Numbered positional replacement fields: Explain how the string format method fills numbered replacement fields from positional parameters, regardless of where the fields appear in the format string.
Why it matters: The field number, not its location in the string, determines which value appears. - Literal braces in format strings: Describe how to include brace characters in a format string without making them replacement fields.
Why it matters: Escaping braces lets you display them without having format interpret them as fields.
Key terms: format method, format string, numbered replacement fields, parameters, braces, escapes, replacement field
6.6.2 The format method and named parameters
This section explains how format selects named replacement values and how named and positional parameters can be used together.
- Named format replacement fields: Explain that format chooses a value for a named replacement field by matching the field’s name to a named parameter. Describe how a call can also use positional parameters and access an element of a named parameter, as in user[1].
Why it matters: It lets you identify which value each replacement field will insert.
Key terms: format method, named parameters, replacement fields, positional parameters, user[1]
6.6.3 Format specifiers
This subsection shows how format specifiers control the appearance of values inserted into replacement fields.
- Replacement field format specifiers: Explain how format specifiers control a replacement field’s width, alignment, and padding, using the section’s examples.
Why it matters: Format specifiers let you choose how inserted values appear without changing the values themselves.
Key terms: format specifier, replacement field, fill character, alignment, width
6.7 String interpolation with f-strings
This section explains how f-strings insert evaluated Python expressions into strings and support formatting and debugging output.
- F-string expression interpolation: Explain how to write an f-string and how expressions inside it become part of the resulting string.
Why it matters: F-strings offer a concise, readable way to build strings from values and expressions. - Formatting and debugging expressions: Describe how f-strings can use format specifiers and show an expression alongside its value for debugging.
Why it matters: These features let a reader control displayed values and inspect expressions while creating strings.
Key terms: f-strings, Python expressions, curly braces, interpolated, format specifiers, debugging, expression, value
6.8 Formatting strings with %
This section explains how old-style Python string formatting uses % to substitute values into a new string and why it is mainly useful for reading older code.
- Percent Formatting Substitution: Explain how the string modulus operator uses formatting sequences in a string on the left and values in a tuple on the right to produce a new string.
Why it matters: This lets you recognize what a % formatting expression produces when you encounter one. - Old Style Formatting Context: Explain why the book covers % string formatting while advising against its use in new code.
Why it matters: You may need to read this syntax in existing code without choosing it for new code.
Key terms: string modulus operator, formatting sequences, %s, str, tuple, old style of string formatting, deprecated
6.8.1 Using formatting sequences
Explains how percent-formatting sequences substitute values into strings and control the appearance of a formatted number.
- Percent formatting sequences: Explain where formatting sequences appear in a percent-formatted string and how they specify substitutions. Describe how a numeric sequence can control field width, decimal places, and justification.
Why it matters: This lets you read a percent-formatted string and predict both which values appear and how they will be displayed.
Key terms: formatting sequence, %s, field width, left-justifies
6.8.2 Named parameters and formatting sequences
This section explains named substitutions in %-formatted strings and the basic output controls available through print.
- Named percent-format substitutions: Explain how a %-formatting sequence names a value and how the dictionary on the right of % supplies that value. Describe why this is useful when a format string has many substitutions.
Why it matters: Named substitutions make longer format strings easier to manage without matching values to positions. - Print output controls: Describe what print does with one or several arguments and what aspects of simple output its additional parameters can control.
Why it matters: These options cover simple text-output needs without requiring a more elaborate format string.
Key terms: formatting sequences, string modulus operator, dictionary, named key, print function, newline character, console output
6.9 Bytes
This section distinguishes bytes from strings and explains how to convert between them when working with binary data or text.
- Strings and bytes distinction: Distinguish a string, an immutable sequence of Unicode characters, from a bytes object, a sequence of integers. Explain why bytes are useful for binary data while strings represent text, including text with accented characters.
Why it matters: Choosing between text and bytes matters when your program handles binary files or international text. - Encoding and decoding bytes: Explain that a string’s encode method converts it to bytes and a bytes object’s decode method converts it back to a string. State that bytes and strings cannot be added together directly because they are incompatible types.
Why it matters: Conversion lets you move between text and bytes without treating incompatible types as interchangeable.
Key terms: string, bytes object, Unicode characters, binary data, encode, decode, type error
6.10 Preprocessing text
This lab sets out a text-preprocessing task and suggests a memory-conscious, step-by-step way to complete it.
- Text cleaning workflow: Describe how to turn the source text into cleaned output: make it one case, remove punctuation, split it into words, and write one word per line to a second file.
Why it matters: Cleaning the text first makes later word counting easier. - Incremental line processing: Explain why the section suggests processing the file line by line and checking each cleaning step before adding the next.
Why it matters: This approach makes the lab easier to check without holding the whole file in memory.
Key terms: normalize, punctuation, splitting the text into words, line by line, one cleaning step at a time
6.10.1 Solving the problem with AI-generated code
This section shows how to specify a text-preprocessing task in an AI prompt and check the generated code.
- AI text preprocessing prompt: Describe the requirements to give an AI tool for preprocessing moby_01.txt, and state that the generated code should be tested.
Why it matters: A specific prompt gives the AI tool the task’s required steps, while testing checks whether its code actually works.
Key terms: prompt, AI tool, moby_01.txt, moby_01_clean.text
6.10.2 Solutions and discussion
6.11 Summary
Where this is used later
- Regular expressions and raw strings (chapter 16) · Python string escape sequences
- Functions to read and write text or binary data (chapter 13) · Reading and writing text may involve lines that already contain newlines.
- Unstructured text (chapter 21) · Using the string split method
- Delimited flat files (chapter 21) · The string split method.
- What is a regular expression? (chapter 16) · Introduces regular expressions as the pattern-searching alternative mentioned here.
- Methods (chapter 15) · User-defined class methods provide the later context in which the distinction between representations becomes important.
- Using binary mode (chapter 13) · Reading and writing in binary mode depends on distinguishing bytes from text.
- Text encoding: ASCII, Unicode, and others (chapter 21) · Discussion of text encoding builds on the distinction between Unicode text and bytes.
Concepts explained in this chapter
- stringA string is an immutable sequence of characters represented as a Python value.
- joinjoin is a Python string method that concatenates strings from an iterable, placing the invoking string between adjacent elements.
- replace methodreplace is a Python string method that returns a copy with occurrences of a specified substring replaced by another string, optionally limiting the number of replacements.
- translatetranslate is a Python string method that returns a copy of a string with characters transformed or removed according to a translation table.
- translation tableA translation table is a mapping from source characters to replacement characters used for character-by-character text translation.
- concatenationConcatenation is the operation of joining ordered sequences end to end to form a single sequence.
- splitsplit is a string method that divides a string into a list of substrings according to a separator or, by default, whitespace.
- format stringA format string is a string containing replacement fields that specify how supplied values are incorporated into formatted text.
- string modulestring is a Python standard-library module providing constants and utilities for common string-processing tasks.
- format operatorThe format operator is Python's percent operator when applied to a format string and values to produce formatted text.
- formatting sequenceA formatting sequence is a percent-prefixed pattern in a format string that specifies how a corresponding value is converted and inserted into the resulting text.
- newlineA newline is a character or character sequence that marks the boundary between consecutive lines of text.
- immutable collectionImmutability is the property of an object or data structure that prevents its contents from being changed after creation.
- loopA for loop is a control-flow construct that iterates over the items supplied by an iterable.
- punctuationPunctuation is the set of written symbols that structures or separates language apart from letters and digits.
- rere is Python's standard-library module for matching and processing text with regular expressions.
- regular expressionA regular expression is a formal pattern notation for matching and processing text strings.
- UnicodeUnicode is a character-encoding standard that assigns code points to characters across the world's writing systems and many symbols.
- methodA method is a function associated with a class or object and invoked in the context of that class or object.
- escape sequenceAn escape sequence is a character sequence in a string literal that represents a character or control function using a backslash-based notation.
- MapA map is an associative data structure that stores key-value pairs and supports retrieving values by key.
- delimiterA separator is a formatting character inserted between groups of digits in a structured text value.
- trimstrip is a Python string method that returns a copy of a string with specified characters removed from both ends, defaulting to whitespace.
- normalizationNormalization is the process of converting data into a consistent canonical representation.
- membership operatorThe in operator is a Python comparison operator that tests whether a value is contained in a collection.
- encodingencode is a string method that converts text into a bytes object using a character encoding.
- slicingSlicing selects or partitions a portion of an indexed sequence or multidimensional array, often along specified indices or dimensions.
- ASCIIASCII is a character-encoding standard that assigns numeric codes to a defined set of English letters, digits, punctuation marks, and control characters.
- positional argumentA positional parameter is a function argument identified by its ordinal position in the argument list.
- list()The list function is a Python built-in constructor that creates a new list from an iterable or sequence of elements.
- floating-point arithmeticA floating-point number is a numeric value represented using a finite-precision floating-point format.
- integerAn integer is a whole-number value with no fractional component and, in Python, arbitrary magnitude subject to available memory.
- code generationCode generation is the automated production of source code from prompts, specifications, templates, or other inputs.
- promptA prompt is an instruction or input supplied to an AI system to elicit a particular response or generated output.
- parsingParsing is the analysis of input according to structural or syntactic rules to identify its components and produce a structured, usable representation.
- binary dataBinary data is information represented as encoded bytes rather than directly as human-readable text characters.
- tab characterA tab character is a whitespace character that represents horizontal spacing in source text.
- tupleA tuple is an ordered, immutable collection that can contain multiple values, including values of different types.
- dotted notationDot notation is syntax that accesses an attribute or method by placing its name after an object and a period.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
