Chapter 17: Data types as objects
The introduction explains that Python’s dynamic typing makes types relevant at runtime, not just the objects they describe.
After this chapter you will be able to
- Runtime types in Python. Explain that Python is dynamically typed: types are determined at runtime rather than compile time, which makes it possible and sometimes necessary to compute with the types of objects.
- Types and classes as objects. Explain what kind of value type returns and how the section relates Python types to classes.
- Comparing Python type objects. Explain that comparing the types of two values shows whether they have the same type, and give the section’s string and integer examples. Describe type checking in functions or methods as one possible use.
- Classes as inspectable objects. Explain that an instance’s class is itself a Python object that can be stored or passed around. Describe how __class__, __name__, and __bases__ reveal an instance’s class and its inheritance structure.
- Instance and subclass checks. Distinguish isinstance, which checks an instance against a class, from issubclass, which checks class types. Explain that an instance of a derived class also counts as an instance of its base class, while the reverse does not hold.
- Interface Over Inheritance. Explain how duck typing determines whether an object can be used for an operation, using the section’s iterator example.
- Special method attributes. Explain what makes a class method a special method attribute, how it is named, and how Python uses it in response to an operation on an object.
- User-readable strings with __str__. Describe what __str__ does in the RGB color example and why printing an instance can use it without an explicit call.
- Memory-safe list-like file access. Explain why reading the whole file at once may fail and how reading one line at a time avoids that problem. Describe the desired result: using the file like a list of lines in a for loop without loading it all into memory.
- List-Like Access with Getitem. Explain how defining `__getitem__` lets instances of a user-defined class respond to indexed access and be used in a `for` loop. Describe how `LineReader` reads one file line per iteration and raises `IndexError` when an empty string signals the end of the file.
- Indexed access and loop termination. Trace how indexed access calls `__getitem__` and how a `for` loop can request successive elements until `IndexError` ends the loop.
- LineReader sequence imitation. Explain how `LineReader` supplies file lines to a `for` loop and why its ignored index makes it a limited imitation of a sequence.
- Sequential LineReader index handling. Explain why LineReader should accept only sequential indexes and raise an error for other accesses.
- Special methods for list behavior. Describe how defining special method attributes lets a class respond to list-like operations, using assignment and concatenation as examples.
- TypedList initialization and checking. Describe how the example element and optional initial list are used when a TypedList is created.
- Indexed access and assignment. Trace how TypedList handles an indexed read and an indexed assignment through special methods.
- Subclassing as a practical alternative. Explain why someone creating a list-like structure might consider subclassing list or UserList instead of implementing one from scratch.
- Typed lists through subclassing. Explain why subclassing list avoids reimplementing list operations and how the example checks the type of an assigned item.
- UserList subclassing and data access. Explain how subclassing UserList differs from subclassing list: UserList is a wrapper class in collections, and its underlying list is available to the subclass through the data attribute.
- Choosing built-in-like class behavior. Explain when to add selected special method attributes to a frequently used class and when to consider implementing the full built-in-like behavior or subclassing a built-in type. Describe why inconsistent behavior, such as indexing working on one sequence-like object but not another, can confuse other programmers.
- String-only dictionary enforcement. Explain why overriding __setitem__() alone is insufficient for a string-only dictionary and describe the suggested way to check both assignments and initial contents.
- AI prompt and method requirements. Explain why the prompt should specify a subclass of dict, and state that the generated solution must handle both __setitem__() and __init__() correctly.
What it builds on
- Defining classes (chapter 15) · Introduces creating user-defined data types with classes.
- Inheritance (chapter 15) · Class inheritance and base classes
- Optional type hints in Python (chapter 4) · The earlier introduction to optional type hints provides context for the alternative mentioned here.
- Methods (chapter 15) · Assumes familiarity with methods defined in classes.
- Functions to read and write text or binary data (chapter 13) · Assumes familiarity with reading text from files.
- Lists are like arrays (chapter 5) · the basic idea of a Python list
Section by section
Introduction
The introduction explains that Python’s dynamic typing makes types relevant at runtime, not just the objects they describe.
- Runtime types in Python: Explain that Python is dynamically typed: types are determined at runtime rather than compile time, which makes it possible and sometimes necessary to compute with the types of objects.
Why it matters: This is the reason the chapter treats types as something Python code can work with.
Key terms: dynamically typed, runtime, compile time, types of objects
17.1 Types are objects too
This section uses the built-in type function to show that Python types and classes are themselves objects.
- Finding an object's type: Describe what the built-in type function returns for a Python object, using the section's integer and list examples.
Why it matters: This gives you a way to examine the types that the section then treats as objects. - Types and classes as objects: Explain what kind of value type returns and how the section relates Python types to classes.
Why it matters: It connects familiar classes to Python's broader idea that types are objects too.
Key terms: type function, int, list, type object, class, <class 'type'>
17.2 Using types
This section shows that Python type objects can be compared to check whether values have the same type.
- Comparing Python type objects: Explain that comparing the types of two values shows whether they have the same type, and give the section’s string and integer examples. Describe type checking in functions or methods as one possible use.
Why it matters: Comparing types offers a way to check what kinds of values a function or method receives.
Key terms: type objects, type checking
17.3 Types and user-defined classes
This section shows how to inspect the class of an object and its inheritance relationships, then use isinstance and issubclass for common type checks.
- Classes as inspectable objects: Explain that an instance’s class is itself a Python object that can be stored or passed around. Describe how __class__, __name__, and __bases__ reveal an instance’s class and its inheritance structure.
Why it matters: It lets you inspect an object’s class and trace where that class comes from. - Instance and subclass checks: Distinguish isinstance, which checks an instance against a class, from issubclass, which checks class types. Explain that an instance of a derived class also counts as an instance of its base class, while the reverse does not hold.
Why it matters: These checks help code decide whether it can treat an object as an expected type.
Key terms: class object, __class__, __name__, __bases__, isinstance, issubclass, inheritance
17.4 Duck typing
This section explains duck typing as using objects according to the behavior they provide rather than their inheritance, and weighs its flexibility against the risk of undetected bugs.
- Interface Over Inheritance: Explain how duck typing determines whether an object can be used for an operation, using the section’s iterator example.
Why it matters: This lets you recognize when an object is suitable because of what it does, not what it inherits from. - Duck Typing Tradeoffs: Describe the section’s advice for working with duck typing and the reason some codebases restrict it.
Why it matters: The same flexibility that makes code adaptable calls for care when checking that objects behave as expected.
Key terms: duck typing, interface, inheritance hierarchy, iterator, type-checking, type hints, type checker
17.5 What is a special method attribute?
The section explains how Python automatically calls specially named class methods and uses __str__ to show how a class can hook into built-in behavior.
- Special method attributes: Explain what makes a class method a special method attribute, how it is named, and how Python uses it in response to an operation on an object.
Why it matters: This explains how ordinary-looking operations can use behavior defined by your own class. - User-readable strings with __str__: Describe what __str__ does in the RGB color example and why printing an instance can use it without an explicit call.
Why it matters: The example makes Python's automatic use of special methods observable in a familiar operation.
Key terms: special method attribute, dunder methods, magic methods, __str__, user-readable string representation, print
17.6 Making an object behave like a list
The section motivates list-like access to a large text file by contrasting whole-file loading with reading one line at a time.
- Memory-safe list-like file access: Explain why reading the whole file at once may fail and how reading one line at a time avoids that problem. Describe the desired result: using the file like a list of lines in a for loop without loading it all into memory.
Why it matters: This tradeoff explains why a list-like interface is useful even when the file cannot be held in memory.
Key terms: text file, list of lines, for loop, one line at a time
17.7 The __getitem__ special method attribute
The section shows how `__getitem__` lets a `LineReader` act like a list-like iterable and explains why wrapping file-reading details in a class can be useful.
- List-Like Access with Getitem: Explain how defining `__getitem__` lets instances of a user-defined class respond to indexed access and be used in a `for` loop. Describe how `LineReader` reads one file line per iteration and raises `IndexError` when an empty string signals the end of the file.
Why it matters: It explains how a custom object can work with familiar indexing and loop syntax. - Encapsulated File Reading: Justify putting `LineReader` in a class despite the extra initial code: it handles opening, line-by-line reading, and closing, making work with large one-record-per-line text files easier and less error prone.
Why it matters: Keeping file-handling details inside the class can make programs that use it simpler and less error prone.
Key terms: __getitem__, LineReader, IndexError, iterable, myutils module, one-record-per-line
17.7.1 it works
This subsection explains how `__getitem__` lets a file-reading object act like a sequence in a `for` loop, and why the example is limited.
- Indexed access and loop termination: Trace how indexed access calls `__getitem__` and how a `for` loop can request successive elements until `IndexError` ends the loop.
Why it matters: It explains why an object with `__getitem__` can be used in this kind of loop. - LineReader sequence imitation: Explain how `LineReader` supplies file lines to a `for` loop and why its ignored index makes it a limited imitation of a sequence.
Why it matters: It shows both how little code can produce sequence-like behavior and why that behavior does not make the object a full list.
Key terms: __getitem__, index, for loop, IndexError, LineReader, __init__, fileobject
17.7.2 Implementing full list functionality
This section explains how LineReader could enforce sequential access and how special methods can give user-defined objects more list-like behavior.
- Sequential LineReader index handling: Explain why LineReader should accept only sequential indexes and raise an error for other accesses.
Why it matters: The index rule preserves the reason for using LineReader instead of loading the whole file. - Special methods for list behavior: Describe how defining special method attributes lets a class respond to list-like operations, using assignment and concatenation as examples.
Why it matters: It shows why supporting one kind of access does not make an object a complete list substitute.
Key terms: LineReader, __getitem__, index, special method attributes, __setitem__, __add__, list emulation
17.8 Giving an object full list capability
The section develops a TypedList example to show how special methods can give a type-restricted object list-like behavior.
- Purpose of a typed list: Explain why a programmer might want a list that accepts only one type of element, and how a type mismatch can help identify an error.
Why it matters: This explains the practical reason for building the section’s TypedList example. - TypedList initialization and checking: Describe how the example element and optional initial list are used when a TypedList is created.
Why it matters: The example value is a type specification, not an item silently added to the list. - Indexed access and assignment: Trace how TypedList handles an indexed read and an indexed assignment through special methods.
Why it matters: These methods make ordinary bracket syntax work while preserving TypedList’s type restriction. - Remaining list-like operations: Describe why indexed access alone does not make TypedList fully list-like, and identify the additional kinds of operations the section says it would need.
Why it matters: Supporting one piece of list syntax is not the same as supporting the full list interface.
Key terms: typed list, homogeneity, TypedList, example_element, __init__, type, isinstance, __getitem__, __setitem__, __check
17.9 Subclassing from built-in types
This section contrasts the work of building a list-like class from scratch with subclassing an existing list type.
- Subclassing as a practical alternative: Explain why someone creating a list-like structure might consider subclassing list or UserList instead of implementing one from scratch.
Why it matters: It explains why the book turns from building list behavior manually to considering existing types.
Key terms: list-like class, list type, UserList type, subclassing
17.9.1 Subclassing list
This section shows how subclassing Python’s list can create a typed list by changing only the behavior that needs a type check.
- Typed lists through subclassing: Explain why subclassing list avoids reimplementing list operations and how the example checks the type of an assigned item.
Why it matters: Subclassing lets you preserve existing list operations while changing only the behavior your variation requires.
Key terms: subclass, list type, __setitem__, built-in type
17.9.2 Subclassing UserList
This section presents subclassing collections.UserList as an alternative way to create a variation on a list, emphasizing access to its underlying list through the data attribute.
- UserList subclassing and data access: Explain how subclassing UserList differs from subclassing list: UserList is a wrapper class in collections, and its underlying list is available to the subclass through the data attribute.
Why it matters: Direct access to the underlying list can be useful when implementing a list variation.
Key terms: UserList, collections module, data attribute
17.10 When to use special method attributes
This section gives practical guidelines for deciding when to add special method attributes to a class and when subclassing a built-in type may be preferable.
- Choosing built-in-like class behavior: Explain when to add selected special method attributes to a frequently used class and when to consider implementing the full built-in-like behavior or subclassing a built-in type. Describe why inconsistent behavior, such as indexing working on one sequence-like object but not another, can confuse other programmers.
Why it matters: Choosing a consistent interface makes a custom class less surprising to people who use it. - Informative string representation exception: Explain why defining __str__ can be useful even though the section’s guidelines are not hard-and-fast rules.
Why it matters: A useful printed representation can make debugging easier even when the class does not imitate a built-in type.
Key terms: special method attributes, built-in type, subclass, indexing notation, __str__, print(instance), debugging
17.11 Creating a string-only key-value dictionary
This section outlines how to subclass dict to allow only string keys and values, including when the dictionary is initialized.
- String-only dictionary enforcement: Explain why overriding __setitem__() alone is insufficient for a string-only dictionary and describe the suggested way to check both assignments and initial contents.
Why it matters: Checking assignments does not guarantee that a dictionary’s starting contents meet the same restriction.
Key terms: dict, __setitem__(), __init__(), UserDict
17.11.1 Solving the problem with AI-generated code
This subsection advises how to prompt an AI to generate a dictionary subclass and identifies two methods the solution must handle.
- AI prompt and method requirements: Explain why the prompt should specify a subclass of dict, and state that the generated solution must handle both __setitem__() and __init__() correctly.
Why it matters: A specific prompt guides the AI toward the recommended approach, while the two method names give you concrete points to check in its answer.
Key terms: subclass dict, prompt, __setitem__(), __init__()
17.11.2 Solutions and discussion
17.12 Summary
Concepts explained in this chapter
- __setitem____setitem__ is a Python special method that defines how an object responds when an item or slice is assigned with subscription syntax.
- isinstanceisinstance is a Python built-in function that tests whether an object is an instance of a specified class or tuple of classes.
- special methodA special method is a Python method with a conventionally reserved name that defines object behavior for language operations or built-in functions.
- __getitem____getitem__ is a Python special method that defines how an object responds when an item or slice is accessed with subscription syntax.
- type checkingType checking is the analysis of program values and operations to detect whether they conform to expected types.
- type functionThe Python type function returns an object's type or creates a new type when called with class-construction arguments.
- inheritanceInheritance is an object-oriented mechanism by which a class derives behavior and structure from another class, forming a hierarchy of related classes.
- atomic typeAn atomic type is a type whose supported accesses and operations are indivisible with respect to concurrent execution and provide defined synchronization semantics.
- typed listA typed list is a list abstraction that restricts its elements to an allowed type or set of types.
- generator expressionA generator expression is a Python expression that lazily produces values from an iteration specification without constructing the complete result collection in memory.
- type errorTypeError is a Python built-in exception class raised when an operation or function receives a value of an inappropriate type.
- __str____str__ is Python's special method for producing an informal string representation of an object.
- IndexErrorIndexError is a Python exception raised when a sequence or indexed access is requested outside its valid range.
- Class_Class_ is a book-specific example class containing methods and class variables for demonstrating namespace lookup.
- __class__ attributeThe __class__ attribute references the class object associated with a Python object.
- superclassA base class is a class from which another class inherits attributes and behavior.
- MapA map is an associative data structure that stores key-value pairs and supports retrieving values by key.
- type hintsA type hint is syntax that annotates the intended type of a value associated with a variable, parameter, return value, or similar program element.
- constructor method__init__ is Python's conventional instance-initialization method invoked when an object is initialized.
- list()The list function is a Python built-in constructor that creates a new list from an iterable or sequence of elements.
- __name__ attributeThe __name__ attribute of a class contains the class's defining name.
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.
