Chapter 15: Classes and object-oriented programming
The chapter introduces Python classes as constructs that can hold both data and code, while leaving the details to later sections.
After this chapter you will be able to
- Classes as data and code. State that Python classes can hold both data and code.
- Classes as data types. Explain that a Python class is effectively a data type and that calling a defined class creates an object of that type, called an instance.
- Instance fields as records. Explain how a class instance can serve as a structure or record whose fields are added and accessed with dot notation.
- Automatic field initialization. Describe how `__init__` initializes a new instance’s fields and how those fields can subsequently be used or overwritten.
- Per-instance variable values. Explain that each Circle instance has its own radius variable, so different circles can have different radius values.
- Explicit instance variable access. Describe how assigning to instance.variable creates an instance variable if needed, and distinguish instance.variable from an unqualified variable name inside a method.
- Methods and instance binding. Explain what a method is, how an instance calls one, and why its first parameter is conventionally named self.
- Method lookup and call conversion. Trace where Python looks for a method and how it turns an instance method invocation into a function call.
- Class variable ownership and creation. Explain that a class variable belongs to the class rather than an instance, is created by assignment in the class body rather than in __init__, and can be accessed through the class name even before any instances exist.
- Class Variable Lookup Fallback. Explain that Python looks for an attribute on the instance first, then for a class variable of the same name, and signals an error only if neither exists. Describe how this can provide defaults without initializing the attribute on every instance.
- Instance Assignment Shadows Class Variable. Trace what happens when c1.pi is assigned: c1 gets its own instance variable, while Circle.pi remains unchanged and c2 can still read it. State that changing the class variable requires accessing it through the class name.
- Static method definition and calls. Explain that a static method is defined with the @staticmethod decorator and can be called without creating an instance, though it can also be called through an instance.
- Class method behavior and benefits. Explain how to define and call a class method, how its implicit class parameter differs from a static method, and why using that parameter avoids hardcoding a class name.
- Shared shape position. Explain why Circle and Square inherit from a general Shape class instead of each defining its own x and y coordinates.
- Base class initialization. Describe how a subclass declares its base class and why Circle and Square call Shape’s initializer, typically using super().__init__(x, y). Distinguish that call from explicitly naming Shape.
- Inherited method lookup. Explain how a Circle instance can use Shape’s move method even though Circle does not define move.
- Shared inherited instance variables. Explain why methods defined in either a superclass or a subclass use the same instance variable when invoked on one subclass instance.
- Inherited class variable shadowing. Trace what happens when an inherited class variable is read through an instance, subclass, or superclass, then assigned through the subclass or instance.
- Circle initialization and values. Describe how Circle can be created without arguments because its __init__ method has default parameters, or with arguments that set the instance’s values.
- Inherited move method lookup. Trace why calling move() on a Circle instance uses Shape’s move() method.
- Python private naming convention. State which method and instance-variable names Python treats as private, and describe the resulting difference between direct outside access and access from a class method.
- Private name mangling. Explain how Python mangles private names and why this discourages accidental access without making deliberate access impossible.
- Property access and flexibility. Explain why a class might use a property instead of a plain instance variable or explicit getter and setter calls. Describe how a property can be introduced later without changing client code that uses dot notation.
- Property getters and setters. Describe how the property and setter decorators define access to a named property, and explain what happens when no setter is supplied. Use the temperature example to describe how the getter and setter can convert values.
- Direct method namespace lookup. Explain which namespaces a method can access directly and the order in which they are searched.
- Instance and class lookup through self. Trace the order in which self gives a method access to instance, class, and superclass names.
- Automatic instance memory reclamation. Explain how Python reclaims an instance’s memory without requiring a destructor.
- External resource cleanup. Distinguish automatic instance memory reclamation from explicitly releasing an external resource, and state the recommended approach for the latter.
- Combined inheritance from multiple parents. Explain that a Python class can inherit from multiple parent classes and, when names do not clash, instances can use definitions inherited directly and indirectly from those parents.
- Shared method name lookup. Trace the section's simple example of resolving a.f(): Python checks A, then searches through B and its ancestors before moving to A's next base class, so F's f is used rather than the f in C or G. Distinguish this example from a universal lookup rule for complex hierarchies.
- Complete document string rendering. Explain how implementing __str__() on the elements lets str() on the <html> element return the entire document by calling each subelement’s __str__() method.
- AI Problem Specification. Explain why this problem was well suited to an AI solution and how the result compared with earlier problems.
What it builds on
- Object-oriented programming (chapter 3) · Introduces object-oriented programming.
- Function parameter options (chapter 9) · Functions can accept arguments and default values.
- Python scoping rules and namespaces (chapter 10) · The distinction between Python namespaces and scoping rules.
- Context managers using the with keyword (chapter 14) · The earlier discussion of context managers using the with keyword.
Section by section
Introduction
The chapter introduces Python classes as constructs that can hold both data and code, while leaving the details to later sections.
- Classes as data and code: State that Python classes can hold both data and code.
Why it matters: This is the basic role of classes that the chapter develops.
Key terms: Python classes, data, code
15.1 Defining classes
This section introduces classes as Python data types and shows the basic shape of a class definition and how to create an instance.
- Classes as data types: Explain that a Python class is effectively a data type and that calling a defined class creates an object of that type, called an instance.
Why it matters: This connects a class definition to the objects you create from it. - Class definition body: Describe how the class statement defines a class and what its body may contain. State the CapCase naming convention for class identifiers.
Why it matters: You can recognize a valid class definition even before it has variables or functions.
Key terms: class, data type, instance, class statement, body, pass, CapCase
15.1.1 Using a class instance as a structure or record
This section shows how class instances can hold record-like fields, how `__init__` initializes those fields, and how initialization differs from object creation.
- Instance fields as records: Explain how a class instance can serve as a structure or record whose fields are added and accessed with dot notation.
Why it matters: This is the basic way to use an instance to hold related data. - Automatic field initialization: Describe how `__init__` initializes a new instance’s fields and how those fields can subsequently be used or overwritten.
Why it matters: Initialization lets an instance have usable field values as soon as it is created. - Creation versus initialization: Distinguish the roles of `__init__` and `__new__` as described in the section.
Why it matters: The distinction prevents confusing an instance’s creation with the initialization of its fields.
Key terms: class instance, structures or records, fields, dot notation, __init__, initialization method, self, radius field, __new__, uninitialized object
15.2 Instance variables
This section explains how instance variables belong to individual objects, how assignment creates them, and why Python requires explicit instance-qualified access.
- Per-instance variable values: Explain that each Circle instance has its own radius variable, so different circles can have different radius values.
Why it matters: This explains how objects of the same class can hold different data. - Explicit instance variable access: Describe how assigning to instance.variable creates an instance variable if needed, and distinguish instance.variable from an unqualified variable name inside a method.
Why it matters: Explicitly naming the instance keeps an object’s data distinct from local method variables.
Key terms: instance variable, Circle, radius, instance.variable, instance variables, __init__, local variable
15.3 Methods
This section explains how Python methods are defined, called with an instance and arguments, and resolved during invocation.
- Methods and instance binding: Explain what a method is, how an instance calls one, and why its first parameter is conventionally named self.
Why it matters: This connects the familiar dot-call syntax to the instance a method operates on. - Method arguments and instance variables: Distinguish a method's local parameter from an instance variable, and describe how an __init__ argument or default can set a Circle's radius at creation.
Why it matters: The distinction explains how a constructor argument becomes data stored on a particular circle. - Method lookup and call conversion: Trace where Python looks for a method and how it turns an instance method invocation into a function call.
Why it matters: This explains both which method runs and how it receives the calling instance.
Key terms: method, bound method invocation, self, __init__, self.radius, radius, default argument values, instance namespace, class, superclasses
15.4 Class variables
The section explains how class variables are defined and accessed, including how a method can refer to one without hardcoding the class name.
- Class variable ownership and creation: Explain that a class variable belongs to the class rather than an instance, is created by assignment in the class body rather than in __init__, and can be accessed through the class name even before any instances exist.
Why it matters: This distinction explains why Circle.pi is available without first creating a Circle. - Class variables inside methods: Describe how a method can access a class variable through the class name, and how self.__class__.pi avoids writing that class name explicitly.
Why it matters: It gives the area method a way to use the class’s pi value without hardcoding Circle.
Key terms: class variable, class body, instance, Circle.pi, Circle.area, __class__, self.__class__.pi
15.4.1 An oddity with class variables
Explains how class variables provide fallback values for instance lookups and why assigning through an instance does not change the class variable.
- Class Variable Lookup Fallback: Explain that Python looks for an attribute on the instance first, then for a class variable of the same name, and signals an error only if neither exists. Describe how this can provide defaults without initializing the attribute on every instance.
Why it matters: It explains why an attribute read can succeed even when that instance has no such variable. - Instance Assignment Shadows Class Variable: Trace what happens when c1.pi is assigned: c1 gets its own instance variable, while Circle.pi remains unchanged and c2 can still read it. State that changing the class variable requires accessing it through the class name.
Why it matters: It prevents an assignment to one object from being mistaken for a change shared by all objects.
Key terms: instance variable, class variable, default values, Circle.pi, c1.pi, c2.pi
15.5 Static methods and class methods
Introduces static methods and class methods as topics for the following subsections.
15.5.1 Static methods
This subsection explains how to define a static method and call it without creating a class instance.
- Static method definition and calls: Explain that a static method is defined with the @staticmethod decorator and can be called without creating an instance, though it can also be called through an instance.
Why it matters: This helps you recognize when a method can be called without first making an object.
Key terms: static methods, class instance, @staticmethod decorator
15.5.2 Class methods
This section explains how class methods receive their class automatically and why that helps methods work with subclasses.
- Class method behavior and benefits: Explain how to define and call a class method, how its implicit class parameter differs from a static method, and why using that parameter avoids hardcoding a class name.
Why it matters: Using cls makes a class-level operation work for subclasses without naming the original class inside the method.
Key terms: class method, @classmethod, cls, static method, subclasses, total_area
15.6 Inheritance
This section uses shapes to show how Python inheritance shares instance data and methods, and why subclasses must explicitly initialize their base class.
- Shared shape position: Explain why Circle and Square inherit from a general Shape class instead of each defining its own x and y coordinates.
Why it matters: Inheritance lets related classes share what they have in common without duplicating it. - Base class initialization: Describe how a subclass declares its base class and why Circle and Square call Shape’s initializer, typically using super().__init__(x, y). Distinguish that call from explicitly naming Shape.
Why it matters: Declaring inheritance alone does not initialize the coordinates that these shapes need. - Inherited method lookup: Explain how a Circle instance can use Shape’s move method even though Circle does not define move.
Why it matters: A shared behavior such as moving a shape can be written once in the base class and used by its subclasses.
Key terms: inheritance, Shape, Circle, Square, x and y variables, super, __init__, super().__init__(x,y), Shape.__init__(self, x, y), inheritance hierarchy
15.7 Inheritance with class and instance variables
This section explains how inherited methods share an instance’s variables and how assignments through a subclass or instance can shadow an inherited class variable.
- Shared inherited instance variables: Explain why methods defined in either a superclass or a subclass use the same instance variable when invoked on one subclass instance.
Why it matters: It helps you predict what inherited methods will read or change on an object. - Inherited class variable shadowing: Trace what happens when an inherited class variable is read through an instance, subclass, or superclass, then assigned through the subclass or instance.
Why it matters: It prevents you from mistaking a new subclass or instance value for a change to the superclass’s class variable.
Key terms: inheritance, instance variable, superclass, subclass, class variable, derived class, name clashes
15.8 Recap: Basics of Python classes
This section brings together basic class features in a Shape and Circle example and describes how Circle instances behave.
- Circle initialization and values: Describe how Circle can be created without arguments because its __init__ method has default parameters, or with arguments that set the instance’s values.
Why it matters: It connects constructor arguments to the values held by an individual object. - Inherited move method lookup: Trace why calling move() on a Circle instance uses Shape’s move() method.
Why it matters: It shows how a subclass can use behavior defined by its base class. - Tracked instances and total area: Explain that initialization adds each instance to a list held in a class variable, and state how the total_area() class method can be called.
Why it matters: The example contrasts information kept at the class level with values set on an instance. - Static circle area method: Describe how circle_area() can be called and what makes it a static method in this example.
Why it matters: It distinguishes a static method from methods that receive an instance or class automatically.
Key terms: Circle, __init__, default parameters, instance, Shape, move(), inheritance hierarchy, class variable, total_area(), class method
15.9 Private variables and private methods
This section explains why classes use private names, how Python marks them, and why name mangling discourages accidental rather than deliberate access.
- Reasons for private names: Explain how private variables and methods mark a class’s internal implementation, discourage outside access, and avoid name clashes in inheritance.
Why it matters: Private names help readers tell a class’s intended interface from its internal implementation. - Python private naming convention: State which method and instance-variable names Python treats as private, and describe the resulting difference between direct outside access and access from a class method.
Why it matters: The naming rule lets you recognize which attributes are intended for a class’s internal use. - Private name mangling: Explain how Python mangles private names and why this discourages accidental access without making deliberate access impossible.
Why it matters: Understanding mangling prevents you from mistaking Python’s private names for an unbreakable access barrier.
Key terms: private variable, private method, inheritance, interface, double underscore, private, instance variable, method, name mangling, _classname
15.10 Using @property for more flexible instance variables
This section explains how properties preserve dot-notation access to instance variables while letting methods control the values read and written.
- Property access and flexibility: Explain why a class might use a property instead of a plain instance variable or explicit getter and setter calls. Describe how a property can be introduced later without changing client code that uses dot notation.
Why it matters: Properties let you add control over an attribute while preserving the way other code accesses it. - Property getters and setters: Describe how the property and setter decorators define access to a named property, and explain what happens when no setter is supplied. Use the temperature example to describe how the getter and setter can convert values.
Why it matters: The getter and setter let ordinary reads and writes apply logic such as conversion.
Key terms: property, instance variables, getter and setter methods, dot notation, property decorator, .setter, read-only, getter method, setter, temp
15.11 Scoping rules and namespaces for class instances
This section explains which namespaces a class method can access directly or through self, and how inheritance and private names affect that access.
- Direct method namespace lookup: Explain which namespaces a method can access directly and the order in which they are searched.
Why it matters: This helps you tell where an unqualified name in a method comes from. - Instance and class lookup through self: Trace the order in which self gives a method access to instance, class, and superclass names.
Why it matters: This order helps explain which attribute a method reaches through self. - Private superclass name boundaries: Distinguish private names accessible through self in a class from private names belonging to its superclass.
Why it matters: The distinction explains why a subclass method cannot use self to reach a superclass’s private names. - Class variable assignment targets: Explain why the example uses a class name rather than self to assign to class variables.
Why it matters: Reading a class variable through self and assigning to the class variable are different operations in this example.
Key terms: local namespace, global namespace, built-in namespace, self variable, instance’s namespace, class’s namespace, superclass’s namespace, private superclass instance variables, private superclass methods, private superclass class variables
15.12 Destructors and memory management
This section explains why Python classes rarely need destructors and recommends context managers for releasing external resources.
- Automatic instance memory reclamation: Explain how Python reclaims an instance’s memory without requiring a destructor.
Why it matters: It explains why you normally do not need a destructor to free an instance’s memory. - External resource cleanup: Distinguish automatic instance memory reclamation from explicitly releasing an external resource, and state the recommended approach for the latter.
Why it matters: Automatic memory management does not remove every need to clean up resources.
Key terms: destructor, automatic memory management, reference-counting mechanism, external resource, context manager
15.13 Multiple inheritance
This section explains how Python classes can inherit from multiple parents, how a simple hierarchy resolves a shared method name, and why complex hierarchies require care.
- Combined inheritance from multiple parents: Explain that a Python class can inherit from multiple parent classes and, when names do not clash, instances can use definitions inherited directly and indirectly from those parents.
Why it matters: This is the basic capability that makes multiple inheritance useful. - Shared method name lookup: Trace the section's simple example of resolving a.f(): Python checks A, then searches through B and its ancestors before moving to A's next base class, so F's f is used rather than the f in C or G. Distinguish this example from a universal lookup rule for complex hierarchies.
Why it matters: It explains the result of a method-name clash without treating the simplified search as the whole story. - Readable multiple inheritance hierarchies: Explain why the section cautions against relying on intricate name-resolution behavior and says standard mixin or addin uses can help keep multiple inheritance readable and avoid name clashes.
Why it matters: A working hierarchy is easier to use when its behavior is also easy to understand.
Key terms: multiple inheritance, parent class, ancestor, base classes, method invocation, super, mixin, addin, name clashes, inheritance hierarchies
15.14 HTML classes
This lab defines a simplified HTML document model and specifies how its classes should produce the complete document as a string.
- HTML element representation: Describe the simplified document structure the classes must represent: an <html> element contains a <body> element, and the body may contain text followed by a <p> element containing text.
Why it matters: These limits define the structure the lab’s classes need to handle. - Complete document string rendering: Explain how implementing __str__() on the elements lets str() on the <html> element return the entire document by calling each subelement’s __str__() method.
Why it matters: This is the behavior the lab’s classes are chiefly meant to implement.
Key terms: element, subelement, <html>, <body>, <p>, __str__(), str()
15.14.1 Solving the problem with AI-generated code
This subsection explains why the HTML classes problem worked particularly well with AI-generated code.
- AI Problem Specification: Explain why this problem was well suited to an AI solution and how the result compared with earlier problems.
Why it matters: It connects the clarity of the problem statement with the quality of the AI-generated result.
Key terms: AI solution, sample code and output
15.14.2 Solutions and discussion
15.15 Summary
Where this is used later
- What is a special method attribute? (chapter 17) · Builds on recognizing __str__() as a method that affects an object’s behavior.
Concepts explained in this chapter
- static fieldA class variable is an attribute defined on a class and shared through that class's inheritance structure unless shadowed or otherwise restricted.
- inheritanceInheritance is an object-oriented mechanism by which a class derives behavior and structure from another class, forming a hierarchy of related classes.
- Class_Class_ is a book-specific example class containing methods and class variables for demonstrating namespace lookup.
- attributeAn attribute is a named value or behavior associated with an object and accessed through that object.
- self parameterself is the conventional first parameter name that refers to the instance on which a Python method is invoked.
- superclassA base class is a class from which another class inherits attributes and behavior.
- private memberA private instance variable is an instance attribute represented by a name-mangled identifier intended to restrict direct access from subclasses.
- multiple inheritanceMultiple inheritance is an object-oriented language feature in which a class derives attributes and behavior from more than one parent class.
- mixinA mixin class is a class designed to provide reusable behavior to other classes through inheritance rather than to serve as a complete standalone type.
- virtual methodA virtual method is a method whose implementation can be dynamically selected according to the class of the object on which it is invoked.
- methodA method is a function associated with a class or object and invoked in the context of that class or object.
- name manglingName mangling is Python's transformation of names beginning with two underscores by prepending a class-based prefix to reduce accidental access and inheritance collisions.
- constructor method__init__ is Python's conventional instance-initialization method invoked when an object is initialized.
- __class__ attributeThe __class__ attribute references the class object associated with a Python object.
- namespaceA global namespace is the namespace associated with a module, script, or interactive session and visible as the defining environment for its functions.
- built-in namespaceThe built-in namespace is the namespace containing Python's predefined names, including built-in functions and exceptions.
- local namespaceA local namespace is the namespace containing bindings created within the currently executing function or code block.
- dotted notationDot notation is syntax that accesses an attribute or method by placing its name after an object and a period.
- recordA row is a horizontally aligned record of related values in a tabular data structure.
- HTMLHTML is the HyperText Markup Language, a standard markup language for structuring documents and elements on the web.
- __str____str__ is Python's special method for producing an informal string representation of an object.
- dependencyA software dependency is an external software component or prerequisite that an application or package needs to build or operate.
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.
