
Master modern Python and object-oriented concepts with Python 3.7+, including data classes and enumerations. Set up PyCharm or lightweight editors, plus Jupyter-like tools such as Datalore or Colab.
Define our first classes and solidify concepts like the self attribute, differentiate instance, class, and static methods, and explore class and instance namespaces via the dunder dict, plus attribute access.
Explore Python's PEP 8, the official style guide for code, and learn how readability, maintainability, and clean documented code boost productivity and collaboration.
Define a Python class using the class statement, follow CamelCase naming, and recognize that a class is a blueprint with state and behavior; calling it creates distinct instances.
Set and modify class state by binding attributes inside and outside the class body, access class dict via dunder dict, and note that all instances share these blueprint-defined values.
Learn how to add behavior to a Python class by defining instance methods with self, and see how methods bind to specific instances.
Learn how to define instance attributes in Python using the __init__ method to customize each object's color, set defaults, and distinguish instance from class state.
Learn to access and set object attributes with getattr and setattr as alternatives to dot syntax at scale for programmatic attribute management.
Explore how self represents the instance in Python instance methods, why the first parameter matters, and why self is a convention, not a reserved keyword.
Define a student class with name and age attributes, and allow name-only instantiation via a class attribute platform defaulting to Udemy. Include a grid method that randomly greets from names.
Learn Python object oriented programming by building a student class with a class attribute and instance attributes, a greet and grid method, and a helper to create instances from names.
Learn how to define instance, static, and class methods in Python, decorate with @staticmethod and @classmethod, and call them from class or instance while understanding their parameter differences.
Discover an alternative syntax for static and class methods using built-in tools, see how the class method and static method constructors modify functions, and learn when this approach helps.
Learn how each object stores attributes in a per-instance mapping called __dict__, creating separate namespaces for m1 and m2, while dot access mirrors direct dictionary binding.
Explore how a class's __dict__ becomes a read-only mapping proxy, with string-keyed attributes and descriptors, governing access to class and instance attributes and methods.
Explore how class variables define shared attributes and why mutable class variables can create unintended cross-instance changes; learn to avoid this with immutability and proper bindings.
Explore why Python uses public attributes by default, contrasting with Java-style getters and setters, and show how properties enable controlled access while preserving simple syntax, reflecting the uniform access principle.
Learn how Python docstrings document classes, functions, and modules, accessible via help and bound to the __doc__ attribute; explore pep 257, restructured text, google style, numpy doc, and comments.
Create a password class with strength and length to generate 8, 12, or 16 character passwords using letters, numbers, and punctuation, and include a show input universe method returning character pools.
Define a password class with strength and length, implement an initializer and docstrings, and generate a random password from letters, numbers, and punctuation using a character universe.
Discover how mutable class variables can leak state across instances in Python OOP and fix password generation by copying the list with the copy module to preserve default mid-strength passwords.
Discover python double underscores, called dundas or magic methods, that hook into the python data model to enable pythonic syntax and control equality, string representation, hashability, and truthiness in classes.
Define a book class with title, author, book type, and pages, then customize its representation by implementing a dunder wrapper method to control print output.
Explore __repr__ versus __str__ and the wrapper pattern, showing how __str__ targets end users while __repr__ helps developers recreate instances, with eval and debugging tips.
Define __format__ to customize how objects render in f-strings and format methods, enabling on-the-fly formatting with a format spec that returns a short representation or a full wrapper.
Show how to redefine __eq__ to compare two objects by title and author, with a type check and a nod to duck typing in Python.
Define dunder __eq__ to customize class equality, and consider __ne__ for non-equality, noting that in modern Python __ne__ is not automatically the negation of __eq__ under builtins inheritance.
Explore why lists can't be dictionary keys, explain that immutable objects are hashable, define hashable criteria—comparability, equal hashes, and stable hashing—and show how hashing speeds lookups in dictionaries and sets.
Make the book class hashable by implementing __eq__ and __hash__ using (title, author). Hash values are not meaningful across processes and should align with equality.
Understand how changing attributes can alter an object's hash and break dictionary lookups, and learn how making attributes read-only ensures a stable hash across an instance's life.
Define a contact type that stores name, last name, phone, and email, provides masked and full string representations, equality by phone or email or name, and format-based toggling.
Define a contact class with name, last name, phone, email, and display mode defaulting to masked; enable equality by email/phone or by name and last name, hash, and format-controlled representation.
Define rich comparisons for a book class by implementing __eq__ and __gt__ to compare by page length, with type checks and not implemented fallbacks.
Discover how the functools.total_ordering decorator simplifies Python rich comparisons by defining only __eq__ and one of __lt__, __gt__, __le__, or __ge__, to enable all comparison operators.
Discover how Python truthiness works: objects are truthy by default, and you can customize with __bool__ to make zero or negative pages evaluate as falsy.
Study truth value testing in Python using __len__ to drive boolean logic; see how length reflects pages and determines truthiness, with negatives and __bool__ absence handled.
Build a bookshelf container class that encapsulates books with capacity and type checks. Add books only if they are valid, raising overflow or type errors as needed.
Explore operator overloading in Python by implementing dunder add and radd on a bookshelf class to support plus and plus-equals with books, returning new shelves without mutation.
Explore implementing the __getitem__ magic to access and filter a bookshelf class with Pythonic square bracket syntax, supporting indexing, slicing, and case-insensitive title searches.
Defining your own dunder methods is possible, but discouraged, since custom magic methods risk conflicts with future Python dunders and complicate the class namespace managed by the interpreter.
Explore building a 3d vector class with x, y, z coordinates, supporting magnitude calculation, vector addition, scalar multiplication, comparisons, hashing, and case-insensitive bracket access for coordinates.
Define a 3d vector class with x, y, and z coordinates and nondefault constructor. Implement addition, scalar multiplication, magnitude, comparisons, hashability, truthiness, and getitem access for x, y, or z.
Explore Python object-oriented properties that expose simple data attributes publicly while validating values and supporting read-only and managed attributes through delegated method calculations.
Explore a Python object-oriented programming (oop) approach to modeling customers with a loyalty attribute, using a discount dictionary to calculate and print tier-based percentages while handling missing matches.
Start with plain public attributes in Python and avoid getters and setters; evolve access to attributes only when needed, embracing the Zen of Python: simple is better than complex.
Refactor a Python customer class to enforce valid loyalty levels using a class-level set, implement getters and setters, and use underscore naming to protect attributes from accidental overrides.
Explore how Python uses single leading underscores to signal protected attributes, demonstrate that such attributes remain public and modifiable, and explain name mangling with double underscores to avoid inheritance collisions.
Learn how properties enable validating loyalty levels and membership years in a Python OOP class without breaking client code, keeping dot notation and backward compatibility.
Explore how properties live in the class and act as descriptors that override instance attributes, guiding dot notation to getters and backing variables, with examples of loyalty and dict lookups.
Dna base class accepts a nucleotide input as the name or first letter (A, C, G, T), case-insensitive, validates and standardizes to the lowercase name, raising exceptions on invalid values.
Learn to implement a python dna base class with a validated base stored as a base attribute, standardizing inputs to full lowercase names via a static method for validation.
Learn decorator syntax for python properties, using a getter and setter named like the property to create a clean, equivalent alternative to the built-in property approach, illustrated with loyalty.
Demystify Python decorators from first principles by showing how decorators wrap functions using first-class functions and closures, then decorate with @ syntax and examples like bingo with even/odd.
Explore read-only and write-only properties in Python, showing how the property constructor's parameters are optional, and how to implement getter-only or setter-only properties with backing variables.
Learn how managed properties and computed attributes in Python turn a method into a read-only property to compute a customer's average review on demand, with validation and a caching concept.
Learn how to cache the average review in a Python OOP project and invalidate the cache when a new review is added, ensuring lazy, on-demand calculation.
Learn how to delete properties in Python OOP by implementing a deleter that removes the backing variable from an instance, avoiding attribute errors and preserving the property on the class.
Document python properties with docstrings in the getter to describe the property's behavior, show how to view it with help, and note that setter and deleter docstrings are ignored.
Implement a Python OOP tablet class with validated models (light, pro, max) and auto-inferred base storage/memory (32→64→128; 2→3→4), plus a user editable storage via add_storage or setter, capped at 1024.
Defines a tablet class with light, pro, and max models. Assigns default base memory and storage per model, validates input, and exposes storage via add and read-only properties.
Explore single inheritance from a syntactical perspective, mastering method resolution, order, and subclass overrides, while delegating to a parent and centralizing common behavior for Python OOP.
Leverage inheritance to derive new classes from existing ones, reusing attributes and behaviors, and form is-a hierarchies like virus, RNA virus, coronavirus, and SARS-CoV-2, via single inheritance.
inheritance lets you extend a virus class without duplicating code, enabling RNA virus and DNA virus subclasses to override reproduction while reusing base behavior and creating clear hierarchies.
All Python classes implicitly inherit from object, the base class, making virus-like subclasses callable and able to use default behaviors such as representation and equality, while allowing overrides for customization.
Explore the method resolution order and attribute lookup rules in Python, detailing how the instance, class, and superclasses determine attribute access and how dunder bases and mro expose the chain.
Demonstrate how descendant classes override superclass methods using MRO, where the first match wins. Explore parent-child method interactions, including overriding signatures and delegating mutation logic to subclasses.
Use Python's super to delegate to the parent, enabling indirect access to the parent’s reproduce method and improving maintainability during refactors, with implications for single versus multiple inheritance.
Master how subclass __init__ delegates to the parent via super to initialize common attributes, and understand that Python will call the parent automatically if the subclass does not define __init__.
Develop a bank account hierarchy with an initial balance, deposit and withdraw methods for positive amounts, then add savings, high-interest savings, and locked-in accounts with interest and withdrawal fees.
Define a bank account class with a private balance, a read-only balance property, and methods for deposits and withdrawals, then model savings, high-interest, and withdrawal-fee accounts via inheritance.
Master how subclassing properties works in Python by overriding a parent property's setter with a fully qualified name, enabling subclass-specific validation and delegated getters.
Extend python built-ins by subclassing dict to customize key lookup behavior. Override __getitem__ to replace key errors with random not found messages, demonstrating inherited behavior and selective customization.
Extend python built-ins by subclassing list to create an average list with an average property, supporting list inputs or numeric args, and using sum and len to compute the mean.
Learn how to safely extend Python dictionary behavior by using collections wrappers like user dict to avoid pitfalls when overriding built-ins, ensuring consistent get and getitem behavior.
Use inheritance wisely, but not as the default for every class relationship. Prefer composition for has a relationships and reserve inheritance for is a relationships to build cohesive Python objects.
Explore how to build a bi directional dictionary in Python that enables two ways lookups, maintains unique non mirrored key value pairs, and propagates updates and removals to mirrors.
Create a bidirectional dict with two-way lookup by mirroring bindings and keeping them in sync, using a collections wrapper instead of inheriting dict, and supporting mirrored pop, update, and deletion.
Explore slots in Python objects, learn how to define and use them, and why they exist. Assess benefits with a memory profiler and apply best practices, including inheritance considerations.
Review how instance attributes are stored in Python, compare the instance dictionary with the class namespace, and explain how slots optimize memory and speed by restricting attributes.
Define slots as a class attribute to enable memory and speed optimizations by using a fixed length array instead of a dictionary, then map each attribute to a specific index.
Remove the instance __dict__ by slots, replacing it with a fixed-length storage. Recognize descriptors, including properties, reside in the class mapping proxy and govern attribute access.
Demonstrate how slots reduce memory usage in Python objects by comparing a slotted vs regular employee, quantify about 54% size reduction with a memory profiler, and explain reference objects' role.
Explore how slots affect inheritance: a slotted parent allows the child to use slots and still gain its own dict, but if both are slotted the child loses the dict.
Define a slots class attribute to remove the per-instance __dict__, making all instances lose dicts. Including __dict__ in slotted attributes preserves dicts but adds noticeable performance overhead.
Analyze when to use slots in Python OOP: they save memory and boost performance but remove the instance dictionary and complicate inheritance, so rely on profiling before adopting.
Define a three-dimensional point with x, y, z via slots, extend with inheritance to colored point and shape point, and implement a wrapper to recreate instances.
Define a 3d point class with x, y, z slots and subclasses color point and shape point; override init for keyword args and implement wrappers to recreate instances.
Explore how data classes simplify creating data encapsulation for objects like electric vehicles, reducing boilerplate compared with lists, tuples, and dictionaries.
Use namedtuple from collections to create tuple-like data types with defaults on the rightmost fields, and access attributes directly while supporting a dict-like _asdict conversion and a string-based attribute list.
Named tuples are immutable; reassigning attributes raises an exception. The lecture explains that lists are reference types and suggests using tuples, frozen lists, or data classes for deep immutability.
Explore typed namedtuple from typing, its immutability and named field access, compare it with collections namedtuple, and learn default value rules and type hints for editors and type checkers.
Introduce data classes, added in Python 3.7, to store state rather than behavior by decorating a class with the dataclass decorator from the dataclasses module and reduce boilerplate.
Learn how data classes reduce boilerplate, auto-generate dunder init, provide wrapper and built-in equality and ordering, and improve maintainability for data-centric Python objects.
Learn how Python type hints guide editors and readers, with data class fields requiring annotations, while runtime remains dynamic and tools like mypy enforce typing.
Customize Python data class fields with defaults and the field function, control representation and comparison, and implement inverse price ordering with the total_ordering decorator.
Define a luxury field as a dynamic boolean in a data class using __post_init__, determine luxury by price and make, and switch the default to none to allow user override.
Explore how data classes become immutable by using frozen, making instances hashable, and controlling which fields participate in the hash, so they can serve as dictionary keys or set members.
Understand how data classes inherit fields from a parent to a child, with later definitions shadowing earlier ones and defaults shaping the final field set along the inheritance chain.
Compare named tuples and data classes to choose the tool for Python object oriented programming. Named tuples are immutable, value-based tuples, while data classes are flexible, extensible classes with defaults.
Define stock, position, and portfolio as data classes, then compute value and yield from price, shares, and annual dividends, with value-based comparisons and total ordering.
Master descriptors in Python to control attribute access, intercept get, set, and delete operations, and unlock powerful, reusable code via the internal machinery behind properties, class methods, and slots.
Explore Python's attribute lookup chain from instance and class dictionaries through the inheritance hierarchy. Learn how descriptors alter lookups and when Python raises attribute errors.
Learn how a descriptor object implements the descriptor protocol with get, set, and delete to intercept and customize attribute access in Python objects.
Explore descriptors to map object attributes to object-relational mapping fields, implement a text field descriptor with length and type validation, and examine binding behavior and descriptor precedence in attribute access.
Use per-instance storage for descriptor values to prevent shared state across objects. Consider hashability, memory leaks, and alternatives like id-based keys or weak key dictionaries from weakref.
Store descriptor fields per instance in the instance dictionary to align storage with the object lifecycle and avoid memory management pitfalls.
Using __set_name__ in descriptors binds class variable names automatically, eliminating repetitive field name arguments. This enables clean, instance-specific storage of descriptor data.
Master Python descriptors by clarifying self, owner, and instance in the descriptor protocol, and guard against none when accessed from the class.
Learn how data descriptors override the instance namespace through the descriptor protocol, while non-data descriptors that implement only __get__ are shadowed by instance attributes.
Explore how properties and descriptors validate type and length, compare reuse and scalability, and reveal why descriptors excel for project-wide consistency while properties suit single-attribute use.
Explore how properties and descriptors connect in Python, comparing decorator syntax with the property built-in. See how a class attribute maps to a descriptor like TextField through the descriptor protocol.
Define a student profile class with name, GRE score, and SAT score, validate scores within GRE and SAT ranges, default 130 and 400, using descriptors for storage and eval-friendly representation.
Explore Python object oriented programming by using data descriptors to validate and bind attributes such as SAT score and city, enforcing type checks and value ranges.
The lecture shows a refactor that creates a base validated score descriptor and uses inheritance for SAT score and GRE score, enabling dynamic naming and validation.
Explore enumerations in Python to encapsulate static value collections and access them efficiently. Learn about members and aliases, uniqueness, automatic value generation, flags, and bitwise operations for practical interfaces.
Learn how an enum solves the problem of managing a static collection of fixed values, using political parties or colors as practical examples.
Enumerations in Python, added in 3.4, define a class inheriting from the enum metaclass to map static values to a single variable, offering immutability, iterability, and hashability.
Explore how Python enums expose members as instances of the enum type, accessible by both symbolic names and their values, and contrast them with regular classes for clear type behavior.
Python enums require unique symbolic names, but multiple names can share the same value as aliases; the first member is the master, and all later aliases point to it.
Enforce uniqueness in Python enums by keeping symbolic names unique while allowing repeated values, and use the unique decorator to raise a value error when duplicates occur.
Introduces a functional syntax for defining enums in Python, showing enum parties and labels, auto-assigned values, and how to override them with tuples or dictionaries.
Explore how Python enums map symbolic names to values, using sentinel objects or auto to create distinct members. Avoid mixing auto with custom integers to prevent aliases.
Learn how to customize Python enum auto values by implementing generate_next_value, override before member definitions, and use a base class to keep enums simple while returning custom objects.
In Python, enums are full types that act like classes and can be extended with new behavior; they are iterable and expose values, but enums with members cannot be subclassed.
Learn how the enum Flag class enables combining text styles like bold, italic, underline, superscript and subscript into a compact bitmask using auto and powers of two to avoid ambiguity.
Learn how bitmasks use flag enums in Python to manage state with bitwise operators—or, and, not, xor—then build a pythonic text style set interface using plus, minus, and in.
Discover how bitwise operations work on binary representations, using and, or, and xor, and see how eight bits form a byte and drive modern computing.
Implement a permission system with a read, write, execute enumeration and bitwise support. Define a user class that assigns permissions by role and supports creation via string or integer inputs.
Define a flag-based permission enum for read, write, and execute. Map roles to permissions and implement a base and user class with permission checks, inference, and bitwise operations.
Explore how Python exceptions form an inheritance hierarchy, distinguish syntax errors from other exceptions, and master propagation and handling with try, except, else, plus nesting and defining exception hierarchies.
Explore how the exception object, an instance of base exception, redirects control flow when raised and demonstrates distinctions between type errors, name errors, zero division errors, and syntax errors.
Discover how Python's try and except blocks stop exception propagation and resume execution, using specific handlers rather than broad catches for reliable code.
Raise exceptions with the raise keyword, raise from within a try block, and use handlers to enforce business logic, such as detecting zero quantity in a portfolio.
Adopt the eafp style in python by attempting an operation such as reading greeting.txt and handling exceptions, contrasting with look before you leap and lbyl checks.
Explore how syntax errors differ from other exceptions: they cannot be caught because they stop compilation to bytecode, while other exceptions can be handled during interpretation.
Explore the Python exception hierarchy from base exception to specific subclasses, learn that catching a parent handles all its subclasses, and practice ordering handlers by decreasing specificity to control propagation.
Explore how the else clause runs only when no exception occurs in the try block, after except handlers, and practice decoding json payloads with error handling.
Discover how the finally block guarantees cleanup code runs after a try, regardless of exceptions or returns, and why using with statements for files is the pythonic alternative.
Explore nesting of exception handlers and how inner and outer handlers interact through exception propagation. Bundle multiple exceptions in a single handler and note that the deepest exception takes precedence.
Define custom exceptions by subclassing base exception to build a domain-specific hierarchy that fits your program, enabling precise handling and integration with Python’s exception propagation flow.
Create a Python object-oriented letter guessing game that randomly picks a letter and tracks time and before/after valid guesses, using a custom exception hierarchy.
Define a custom exception hierarchy for a letter guessing game and implement a game class that tracks performance with a default dict, validates input, and times the session.
Welcome to the best resource online and the only one you need to learn and master object-oriented programming with modern python!
There has never been a better time to learn python. It is consistently ranked in the top 3 most in-demand and most-loved programming languages in the world, with applications in machine learning, web development, data science, automation, game development, and much more. And its growth shows no signs of stopping.
But while there are plenty of resources to learn the basics of python, it is quite difficult to move past those to the intermediate and advanced facets of the language. This course seeks to address that.
Over more than 20 hours of detailed lectures, live coding, and guided projects we will unpack everything that python has to offer, starting from absolute scratch. We will master not only object-oriented python and how to use it, but in the process also gain an understanding of the python data model and the essence of writing pythonic code.
Every five to ten lectures we will stop and practice what we have covered, as we work through a list of detailed requirements and convert that to an object-oriented solution using nothing by zero-dependencies, pure python.
––––– Structure & Curriculum –––––
The curriculum is organized around three parts of increasing target proficiency.
In the first, we will cover the essential foundations of working with classes in python, defining our own types, customizing them using dunders, exposing managed attributes through properties and effectively using inheritance.
· Classes
· Dunders
· Properties
· Inheritance
Having established that core foundation, in the next five sections, we will dive into more advanced topics that effective python developers rely on. These include modern features like dataclasses, enumerations and slots but also more established, pivotal constructs like descriptors and exceptions.
· Slots
· Dataclasses
· Descriptors
· Enumerations
· Exceptions
Then in the final four sections we will take a look under the hood at how python recognizes and works with types. We will explore, practice and implement several patterns including duck typing, dynamic protocols and abstract base classes. Finally, we will look at the internal machinery that produces classes in python, as we turn our attention to class metaprogramming.
· Dynamic Protocols
· Abstract Base Classes
· Multiple Inheritance
· Class Metaprogramming
This course is intended for anyone who is committed to mastering object-oriented programming with python, regardless of prior experience, which is why a full-length bonus introduction to the python programming language is included to get anyone up and running writing pythonic code in no time.
I hope you commit to joining me in this journey as we take your python to the next level. See you inside!