
Explore Python 3 object oriented programming concepts, including classes, objects, initialization, attributes, and dunder methods; learn about single inheritance, descriptors, enumerations, and exceptions.
Develop prerequisite readiness with real-world Python projects and core computer science concepts, including scopes, decorators, and functional and object-oriented fundamentals.
Explore objects and classes in Python, cover instantiation and initialization, distinguish data attributes from methods, and learn binding with instance, class, and static methods, plus properties.
Explore objects and classes as containers of data and behavior, using attributes, methods, and dot notation to access instances and the class type.
Explore objects and classes by defining a simple class, observe that a class is an object of type type, and use the is instance check to verify objects.
Define and manipulate class attributes in Python, using language and version on the class, retrieve with get attribute or dot notation, and set or delete to mutate state.
Define a class and mutate its attributes with dot notation and get/set attribute, such as language and version, and examine how the object's state appears in its mapping proxy.
Explain callable class attributes by showing a class attribute that is a function, and demonstrate calling it via __dict__, getattr, and dot notation on class object, which returns Hello World.
Learn how Python class attributes can be callable by defining a function inside a class, accessing it with dot notation or getattr, and distinguishing data attributes from methods.
Explain how Python classes are callable, perform class instantiation, and differentiate class and instance namespaces, using the dunder dict and the dunder class attribute.
Learn how Python classes are callable, yielding instances, and distinguish class attributes from instance data, using isinstance and type for type checks while avoiding unsafe dunder class manipulation.
Learn how data attributes differ from functions in Python classes, focusing on class vs instance attributes, dictionaries, and attribute lookup across namespaces.
Explore data attributes in Python by creating bank account instances, examining class attributes like APR and account type, and learning how instance attributes hide class attributes.
Explore how function attributes become bound methods in Python, with the instance injected as the first argument, and compare instance methods with regular functions using say hello and language examples.
Understand how a function inside a class becomes a bound method on an instance, with self passed automatically, and see how runtime monkey patching adds new methods.
Instantiate Python classes by creating a new object and its namespace, then initialize it with a bound __init__ that sets instance attributes like version.
Learn how Python creates and initializes class instances using the dunder init method to set initial attributes, binding to the object and handling init arguments.
Create attributes at runtime and bind functions to a specific instance using the types module, converting them into methods that receive self.
Learn how to create attributes and bound methods at run time in Python by monkey-patching instances, using function and method types, and customizing instance behavior.
Explore how Python uses the property class to convert underscore prefixed attributes into controlled getters and setters, enabling validation and backward compatible interface.
Explore how Python uses properties to implement getter and setter logic, from bare attributes to property objects with validation, deleters, and docstrings.
Learn how property decorators define getter and setter on class attributes, with deleter support, using decorator syntax to create and customize property objects.
Master property decorators in Python by building property objects with getter, setter, and deleter, using decorator syntax, and understanding fget, fset, fdel and docstrings.
Learn how read-only and computed properties work in Python, using backing variables and the @property decorator to create lazy, cached attributes like circle area.
Learn how read-only and computed properties work in Python, using circle area caching and radius updates to illustrate cache invalidation, lazy loading, and property decorators.
Delete properties from instances, not the class; removing the underscore color from the instance dictionary uses the delete keyword or the delattr function.
Delve into deleting properties by implementing a deleter alongside getter and setter, using both old-style and decorator syntax, and observe backing variables and the instance dictionary.
Examine how the property class provides a convenient way to define instance properties and class properties, its reuse limitations, and the potential for data descriptors and metaprogramming.
Explore class and static methods, their binding to class or instance, and when to use the class method decorator. See timer and circle examples with time zones and UTC.
Explore how instance methods, class methods, and static methods behave in Python classes, with timer examples, time zone handling, and class-level attributes.
Examine how the class body defines its own scope while functions live in the module scope, and how Python resolves attributes and methods across nested namespaces.
Examine Python class bodies, attributes, and scope by building a language class with major, minor, and revision; compare instance, class, and static methods, and explore closures and nonlocals.
Review the circle class, its class attribute origin, and how changes affect all instances. Summarize initializer, static and class methods, private underscore attributes, doubling radius, and property getters and setters.
Design a bank account system with a time zone class and an account class to manage deposits, withdrawals, non negative balances, and a monthly interest; generate confirmations with UTC timestamps.
Build a time zone class with name and offset, represented by a timedelta. Validate inputs, expose read-only properties, and define equality for reliable comparisons.
Explore generating transaction numbers across accounts with a class counter, a generator, or the EDA Tools module counter, starting at 100 and incrementing with next, highlighting when classes aren’t required.
Implement account number as read-only, and first name and last name with private attributes and properties, using a static name validator and setattr-based setter to ensure non-empty values.
Add a preferred time zone to the account class as an optional argument defaulting to UTC, with a time zone property and setter that enforces a valid time zone object.
Define a bank account with a private balance and a read-only property, initialized to zero, and ensure balance changes occur only via transactions.
Explore implementing a shared interest rate for bank accounts using a private class variable and class methods for get and set, with real-number and non-negative validation.
Explore how to implement transaction codes using a dictionary to map labels such as deposit, withdrawal, and rejected to codes, avoiding hard-coded strings.
Learn to generate and parse confirmation codes for transactions, using transaction code, account number, UTC date time, and transaction id, with time zone handling and a lightweight named-tuple structure.
Explore implementing real-transaction methods in a Python OOP account class by validating deposits and withdrawals, generating confirmation codes, handling balances safely, and preparing for unit testing.
Learn to implement automated testing with unittest in Python, covering unit and integration tests, edge cases, and setup and teardown workflows to ensure robust code.
Explore polymorphism and duck typing in Python, where behavior matters more than type. Discover how special dunder methods power iterables, iterators, and context managers.
Explore the __str__ and __repr__ dunder methods and how Python uses them to display and recreate objects, with fallback behavior when __str__ is absent.
Explore how Python's __repr__ and __str__ methods control object representations, implement them in a person class, and compare their use across print, format strings, and fallbacks.
Learn how to implement arithmetic operators in Python with special methods, covering add, sub, mul, true division, floor division, mod, pow, matmul; explore reflected, in-place, unary operators.
Explore implementing arithmetic operators for a vector class using dunder methods, including addition, subtraction, scalar multiplication, dot product, and in-place variants.
Explore rich comparisons in Python, implementing __lt__, __le__, __eq__, __ne__, __gt__, and __ge__ in your class, and learn how Python uses reflected methods when a comparison returns not implemented.
Explore rich comparisons in Python by implementing eq and lt for a 2d vector class, support tuple comparisons, and leverage the total ordering decorator for clean, consistent ordering.
Explore hashing and equality in Python, focusing on __hash__ and __eq__ for dictionary keys and set elements. Learn default identity behavior and why both methods matter for immutable objects.
Learn how hashing and equality interact in Python, override __eq__ and __hash__ to make objects hashable, and use immutable keys in dictionaries and sets.
Explore how Python assigns truth values to booleans, showing that nonzero numbers are truthy, zero and empty collections are falsy, and custom objects rely on __bool__ or __len__.
Explore how Python determines truth values for objects using __bool__ and __len__, with examples of custom classes, including lists, points, and default truthiness.
Make objects callable by implementing the dunder call method, turning class instances into function-like objects that can maintain state. Enable decorator classes and richer function-like behavior.
Explore how objects become callables in Python by implementing dunder call, analyzing functions, methods, classes, and partials. Learn practical patterns with default dicts, decorators, and callable class examples.
Explore the __del__ method, a class finalizer invoked by the garbage collector before object destruction. Learn unpredictability, risks with references and exceptions, and why context managers are preferred for cleanup.
Explore the Python 3 __del__ method by inspecting reference counts, creating objects, and observing when deletion occurs, including its unreliability with exceptions and circular references.
Learn how Python's format function and the __format__ method format values with a format spec, including numbers and dates, with custom classes delegating to representation when no spec is given.
Explore Python's format function and the __format__ method, learn to format floats and date times with custom specifiers, and understand delegation to existing formatters for clean, reusable code.
Explore modular arithmetic by implementing a mod class in Python that handles residues, congruence, hashability, and arithmetic operations with same modulus or integers.
Watch a practical project solution for building a modular arithmetic class in Python, implementing init checks, residue storage, representation, comparisons, arithmetic operations, in-place variants, and refactoring with the operator module.
Introduce single inheritance in Python and contrast it with multiple inheritance, noting the implicit inheritance from the object class and the plan to explore use cases.
Master single inheritance in a shape hierarchy, and see how shape, polygon, and triangle inherit, extend, and override properties through is-a relationships.
Explore a single inheritance hierarchy for shapes in Python, from shape to ellipse, circle, polygon, rectangle, square, and triangle, and learn to use is subclass, is instance, and type.
Explore how Python 3 classes implicitly inherit from the object class, making object the base for all types. Learn about dunder methods and attributes and how to override them.
Explore how the object class underpins all Python types, reveal subclassing and inheritance, and demonstrate overriding __init__ and __new__, plus how modules and the types module relate.
Explore overriding in Python by redefining parent callables in a subclass, observe how method binding to an instance affects calls, and see how inherited and object methods are resolved.
explains overriding the dunder string method in a subclass, and how Python binding and wrappers determine which inherited method runs across an inheritance chain.
Extend functionality through inheritance by creating a more specialized class, such as a student inheriting from person and gaining a study method, and explore extending with class attributes and methods.
Extend your understanding of Python inheritance by adding new methods and attributes in a child class, illustrated with person and student and abstract class concepts.
Learn how to delegate to parent methods in Python using super, especially for init and other overridden methods, ensuring safe binding and initialization across inheritance hierarchies.
Learn to delegate to parent methods with super in Python, ensuring correct binding, avoiding recursion, and proper initialization through the hierarchy.
Discover how slots in Python store only predefined attributes, avoiding per-instance dictionaries and delivering substantial memory savings and faster attribute access for large numbers of objects, while restricting new attributes.
Explore how slots constrain instance attributes, define read-only properties for longitude and latitude, and distinguish class attributes from instance dictionaries while enforcing keyword-only arguments.
Explore slots in single inheritance and how a subclass inherits slots from the base class. Observe how the instance dictionary coexists with slots, and how to mix them for flexibility.
Examine single inheritance and __slots__ in Python to see how __slots__ affects the instance dictionary, then extend slots in subclasses to control storage and add fields.
Develop a Python 3 oop inventory model with a base resource class and cpu, hdd, ssd subclasses, including validation and allocation methods like claim, free_up, and purchased.
Learn the approach and setup for a Python project using pytest in a virtual environment, with a structured layout (app with models and utils, tests/unit) and testing practices.
Create a validators module with a validate integer function that enforces optional min and max bounds, supports custom error messages, and develops unit tests with Pi test.
Implement a resource class for inventory with validation and read-only properties for name, manufacturer, total, allocated, and available, plus pytest unit tests using fixtures and parameterization for high coverage.
Define a cpu class that inherits from resource, implements init with cores, socket, and power watts, validates inputs, exposes read‑only properties, and includes pytest tests for inventory validation.
Explore the storage class inheriting from resource, with capacity in gigabytes and a read only capacity property. Write and run test_storage unit tests for creation, invalid values, and representation.
Extend the storage model with HDD and SSD classes that inherit from resource and storage, validating 2.5in/3.5in sizes and rpm or interface, with 55 tests and full coverage.
Explore Python descriptors as the underpinning for properties, methods, slots, and functions, and differentiate non-data from data descriptors. Learn to write custom data descriptors and weak references, weak key dictionaries.
Explore descriptors that replace boilerplate by enforcing integer values for x and y with a dedicated integer value descriptor, and learn the descriptor protocol including __get__, __set__, __delete__, and __set_name__.
Explore how Python descriptors implement getters and setters, including class versus instance access, and why descriptor state must be stored per instance to avoid shared values.
Explore how to use data descriptors as instance properties, weighing storing values in the instance dictionary versus a descriptor-owned dictionary, and navigate slots, hashing, and potential memory leaks.
Master how to use data descriptors to manage per-instance properties. Learn __set__ and __get__ handling, storage strategies, and how to avoid conflicts and memory leaks with weak references.
Explore strong and weak references in Python, see how reference counts drive garbage collection, and learn to use the weakref module and weak key dictionaries to prevent memory leaks.
Explain strong references and reference counting in Python, and show how weak references with the weakref module track objects without preventing garbage collection, including weak key dictionaries and their limits.
Use instance properties with a weak key dictionary and weak references to achieve instance-specific storage via data descriptors, handling non-hashable objects, and cleaning up dead entries with callbacks.
Learn to manage instance data with weak key dictionaries and weak references, avoiding memory leaks by using id-based keys, callbacks, and cleanup for hashable and non-hashable objects.
Explore the __set_name__ method introduced in Python 3.6 and learn to store descriptor data in the instance dictionary by attribute name, while distinguishing data and non-data descriptors and their shadowing.
Explore how the __set_name__ method recovers descriptor property names at class creation, enabling robust data descriptors, instance storage, and validation messages for properties like first name and last name.
Explore how property lookup resolves between data and non data descriptors, and how descriptors' get and set methods interact with the instance dictionary in Python.
Explore how data descriptors and non-data descriptors resolve attribute lookups, showing how data descriptors store values in the instance dictionary under the descriptor name, while non-data descriptors permit shadowing.
Explore properties and data descriptors in Python by implementing a property object with get, set, and delete methods, and see how dunder get and dunder set control attribute access.
Explore Python properties and descriptors, implementing getters and setters with decorators, adding validation, and building a MakeProperty data descriptor to mirror built-in behavior.
Create reusable data descriptors to enforce types like int, float, and list in class attributes, using a generic valid type that relies on numbers.real for real numbers.
Apply data descriptors to enforce that polygon vertices are a sequence of Point2D with nonnegative integer coordinates, using an INT descriptor and inheritance to define triangle and rectangle.
Explore how functions become methods in Python through descriptors and the non-data descriptor protocol. See how the __get__ method binds functions to instances, producing bound methods and method objects.
Explore how Python functions act as non-data descriptors, using __get__ to return the function or a bound method, and how to build a custom descriptor mirroring this behavior.
Create two data descriptors, integer field and cow field, with optional min and max constraints. Implement a base validator for inheritance and add unit tests to verify validation logic.
Explore building a data descriptor for integer fields with min and max validation, including __set_name__ and __get__ logic, plus unit testing and test-driven development for flexible class creation.
Refactor descriptor validation with a base validator that handles shared storage, get, and set flow, while integer and count field descriptors override validate through inheritance, all tested with unit tests.
Explore enumerations in Python to manage related constants, grouping options, and the versatile features Python's enum support offers. Learn why enumerations matter and how to use them effectively.
Explore why enumerations provide an immutable collection of related constants with unique names and values. See how lookups by name or value work and why Python's built-in enumeration supports this.
Explore Python enumerations by defining Enum members with arbitrary values, accessing them by name or value, and using is for identity, iteration, and membership checks; note immutability and hashability.
Explore how aliases work in Python enums, where multiple names share a master member, and how lookups, containment, iteration reveal the master and how the unique decorator prevents aliases.
Explore Python enum aliases by defining triangle, rectangle, square, and rhombus with shared values; examine lookups by key or value to the main member, and use Unique to prevent aliases.
Customize enumerations by treating them as classes, adding methods and dunder methods bound to members. Override boolean behavior with __bool__ and extend only memberless enums to preserve immutability.
Explore customizing and extending enums in Python by adding bound methods, overriding dunder methods, implementing ordering and equality, and building two-value enums with associated phrases.
Explore how Python's enum auto assigns values to members via a static generate next value method, defaulting to sequential integers starting at one and noting aliases and last values.
Learn how Python enums compute automatic values with auto, override generate next value, and manage aliases, duplicates, and custom value strategies in enum members.
This Python3: Deep Dive Part 4 course takes a closer look at object oriented programming (OOP) in Python.
MAIN COURSE TOPICS
what are classes and instances
class data and function attributes
properties
instance, class and static methods
polymorphism and the role special functions play in this
single inheritance
slots
the descriptor protocol and its relationship to properties and functions
enumerations
exceptions
metaprogramming (including metaclasses)
COURSE PREREQUISITES
Please note this is NOT a beginner level course. You must have a strong working knowledge of functional Python programming as well as some practical experience developing Python applications in order to fully benefit from this course.
In-depth functional Python programming
functions, closures, scopes, decorators (using and writing them)
zip, sorted, any, all, and the itertools module in general
sequences, iterables, iterators and generators (what they are and how to implement the corresponding protocols)
generators, yield, and context managers
mapping types, hashing and relation to object equality
some prior knowledge of basic OOP concepts
know how to work with Python virtual environments and pip install
available Jupyter Notebook (freely available) to follow along with the course notebooks
how to use git
[Please note that this is not a cookbook style course - I don't show you how to solve specific problems, but rather a broad and in-depth look at how OOP works in the context of Python, that will allow you to apply these concepts and techniques to your own problems.]