
Explore Python fundamentals, built-in types, standard library, and idiomatic practices guided by the Zen of Python and PEPs to become an expert Python developer.
Explore Python 3 fundamentals, including the type hierarchy and core data types, multi-line statements and strings, naming conventions, conditionals, and basic functions, loops, and break and continue behavior.
Explore the Python type hierarchy, focusing on numbers (integers, booleans, floats, decimals, fractions, complex) and collections (lists, tuples, strings, sets, dictionaries), plus callables, generators, and classes.
Master Python's handling of physical and logical newlines in multi-line statements and strings, including implicit and explicit line continuation, backslashes, and triple-quoted strings, with a focus on readability.
Identify valid Python identifiers, noting case sensitivity, start rules, and reserved words, then apply conventions like single and double underscores and name mangling with dunder naming.
Develops conditional logic in Python through if/else blocks, nested conditionals, and elif chains, and introduces the ternary conditional expression for single-line decisions.
Explores Python functions, built-ins and module imports, shows defining and invoking functions with def or lambda, explains parameters, return values, optional annotations, and function objects with polymorphism.
Learn how the while loop repeats a code block while a condition is true, cover infinite loops, break and continue, and the optional while-else construct with practical name-validation examples.
Explore how break, continue, and finally behave inside a Python try loop, including handling zero division errors, the else clause, and how finally executes regardless of exceptions or continue.
Explore Python's for loop, its iterables, and the while alternative; cover range, lists, strings, tuples, and enumerate, with break, continue, and else.
Explore how to build and use Python classes, initialize with __init__, and define properties like width and height. Implement area, perimeter, string representations, equality, and rich comparisons using Pythonic conventions.
Explore memory references, variables, and memory management with reference counting and garbage collection, then examine Python's dynamic typing, mutability, and the idea that everything is an object.
Explore how Python uses memory by treating variables as references to objects stored in memory slots, with addresses and a heap, and learn how id and hex reveal memory addresses.
Explore how Python uses reference counting to manage object lifetimes and track references. Inspect counts with get_ref_count and ctypes to see memory references in action.
Discover how Python's garbage collector cleans circular references that evade reference counting, preventing memory leaks, and how to control or inspect it with the gc module.
Explore dynamic typing vs static typing by comparing how variables reference memory objects in Python and Java, showing how types attach to objects and change over time.
Explore variable reassignment, where assigning a new value creates a different integer object at a new memory address and the reference updates; integers are immutable.
Understand object mutability and immutability in Python by examining how internal state changes without altering memory addresses, noting tuples are immutable even when containing mutable elements.
Demonstrate function arguments and mutability in Python, highlighting how strings stay immutable and safe, while lists and mutable tuple elements may change via passed references.
Understand shared references and mutability in Python: how strings are immutable, how lists are mutable, and how passing references to functions can cause in place changes and side effects.
Explore how Python compares values and identity using the is and == operators, differentiate memory addresses, None handling, and how lists, numbers, and strings may share or differ in identity.
Explore how in Python, almost every concept—types, operators, functions, and classes—are objects with memory addresses. Discover how functions become first class citizens you can assign, pass, and return.
Explore how Python interning reuses small integer objects in CPython, caching -5 to 256 at startup to create shared references and reduce memory overhead.
Explore how Python uses string interning to speed up identifier lookups and reduce memory, and learn when to force interning and when to use is versus == in string comparisons.
Explore peephole optimizations in Python, focusing on compile-time constant expressions that get pre-calculated, short sequences, and converting lists to tuples and sets to frozen sets for faster membership tests.
Explore Python's numeric types, from integers and rationals to reals, complex numbers, and the fifth numeric type—booleans—with floats and the decimal module for precision.
Explore how integers exist in Python as arbitrary-precision objects stored in binary. Understand how sign and bit width affect ranges, memory overhead, and arithmetic performance as numbers grow.
Explore python integer arithmetic: add, subtract, multiply, divide with exponents, and learn how division returns a float, while floor division (div) and modulo (mod) solve long division equation, including negatives.
Learn how Python integers are constructed from numbers and strings, explore binary, octal, and hex bases, and how to convert between bases with truncation and encoding rules.
Discover integer constructors in python, including truncation of floats, booleans, and fractions, and convert numbers across bases using binary, octal, and hexadecimal literals and related functions.
Explore rational numbers and fractions using Python's fractions module and Fraction class, constructing and reducing fractions from ints, floats, and strings, with finite precision and irrational numbers like pi.
Import the fraction class from fractions, create fractions from numerators, denominators, or strings, perform arithmetic that yields reduced fractions, and handle negative numerators while using limit_denominator to approximate pi.
Explore how Python stores real numbers as floats using IEEE 754 double precision, detailing sign, exponent, and 52-bit significand within eight bytes, and why some decimals can't be exact.
Explore how Python floats are represented internally and how the float constructor converts strings, numbers, and fractions; learn why 0.1 isn't exact in binary and how precision affects equality.
Explore floating point equality by examining binary representations, learning to compare numbers with is close using absolute and relative tolerances, and avoiding direct equality tests.
Learn how floats are represented as approximations, and compare them using the is close method with both relative and absolute tolerances to handle equality testing.
Learn how to coerce a float to an integer using truncation, floor, and ceiling, including int constructor behavior and handling negative numbers and inevitable data loss.
Explore truncation, floor, and ceiling in Python by using math.trunc (or trunk), the int constructor, floor, and ceil to convert floats to integers; understand differences with negative numbers.
Explore Python's built-in round for floats, including the n parameter and banker's rounding for ties. Understand rounding to powers of ten and behavior with positive and negative numbers.
Explore Python's built-in round function, its behavior with single or double arguments, banker's rounding, and how to shadow it with an underscore or implement a copysign-based alternative.
Explore decimal numbers in Python using the decimal module to achieve exact base ten representation, control precision and rounding, and manage global or local contexts with context managers.
Import and configure Python's decimal module to control precision and rounding, explore global and local contexts, and observe how round half up differs from round half even in practical examples.
Construct decimals with the decimal module's Decimal class using integers, strings, or tuples, avoiding floats for exact values. Context precision affects arithmetic, not construction, with global and local contexts.
Explore how the decimal constructor accepts integers, strings, tuples, or another decimal, yields zero with no input, and how global and local contexts affect arithmetic precision and float inaccuracies.
Explore decimal operations, noting that div and mod differ from integers and the decimal class uses truncation for negatives, while the math module can cast to floats and reduce precision.
Explore decimal division and modulus, including negative values, via the decimal class, and compare with integers; learn built-in math functions and when floats are used.
Explore how decimal objects impose memory overhead and slower arithmetic compared to floats, with limited support for some math functions; choose decimals only when precision matters.
Learn complex numbers in Python using the built-in complex class, real and imaginary parts, literals with j, and polar-rectangular conversions with cmath, plus the is close method for Euler's identity.
Explore complex numbers in Python by constructing real and imaginary parts, using j notation, and applying arithmetic, conjugation, and polar-rectangular conversions via cmath tools.
Explore the boolean data type in Python, its inheritance from int, and how true and false behave as singleton bool objects alongside integer operations, truth values, and type conversions.
Explore how Python assigns truth values to objects, distinguishing truthy and falsy results for numbers, None, zero, empty sequences and mappings, and custom classes via __bool__ or __len__.
Examine how Python evaluates truth values using __bool__ and __len__ methods, covering numbers, sequences, mappings, and short-circuiting in conditional statements.
Dive into booleans, operator precedence, and short-circuit evaluation in Python, with truth tables and De Morgan's laws, plus practical checks for a stock watch list and nullable strings.
Explore boolean operators precedence and short-circuiting in Python, showing how not, and, or evaluate, how parentheses clarify logic, and how short-circuiting prevents errors via truthiness.
Learn how Python boolean operators use truthiness, with short-circuiting in and or and returning operands, not booleans, for practical defaults and safe expressions.
Discover how Python boolean operators work, including short-circuiting with or and and. Apply these concepts to set default values, handle truthy and falsy inputs, and use not to negate truth values.
Explore Python's comparison operators, including identity, membership, and value and ordering tests across numeric types. Learn how chaining and short-circuiting work in practice.
Explore the formal difference between arguments and parameters, distinguish positional and keyword-only arguments, and master default values, mutable type pitfalls, and both unpacking and extended unpacking in Python functions.
Clarify how parameters are local variable names and how arguments are values passed to a function, using myfunc and memory addresses to illustrate passing by reference.
Master how positional and keyword arguments map to function parameters, define default values for optional parameters, and use named arguments to control call order with clarity.
Master positional and keyword arguments in a three-parameter function by mapping A, B, and C. See how default values make parameters optional and how keyword arguments must match parameter names.
Unpack iterables into variables by using comma separation to create tuples, explain single-element and empty tuples, and discuss how dictionaries and sets are unordered during unpacking.
Explains unpacking iterables into tuples, clarifies comma vs parentheses, demonstrates single-element and empty tuples, and covers parallel assignment, swapping, and unpacking lists, sets, dictionaries, and strings.
Master extended unpacking in Python with the star operator for left- and right-hand side splits. Learn nested unpacking, merging dictionaries with the double star operator, and ordering caveats across iterables.
Explore extended unpacking in Python, comparing slicing and start expressions, with practical examples for lists, strings, sets, and dictionaries. Learn nested unpacking and combining iterables to write clearer, flexible code.
Examine how function parameters map to arguments using positional unpacking and star expressions, with extra positional arguments collected into a tuple by *args.
Explore star expressions and function parameters with *args to pack and unpack arbitrary arguments, learn to compute averages safely, handle empty inputs, and unpack iterables into function calls.
Dive into Python keyword arguments, including positional versus named parameters, using star and star args, and enforcing mandatory and optional keyword arguments for robust function calls.
Explore how to define and call Python functions with positional and keyword arguments, control flow with star args and keyword-only parameters, and default values for robust signatures.
Explains how to use star args and star kwargs to collect positional and keyword arguments, storing them in a tuple and a dictionary, and reviews order and keyword-only constraints.
Consolidate Python function parameter concepts by detailing positional arguments, star args, keyword-only arguments, and star star kwargs, including default values and mandatory or optional cases with practical examples.
Explore how Python handles function parameters with positional arguments, *args, and **kwargs, including defaults and keyword-only arguments, demonstrated through practical examples.
Time a function with a generic timer that accepts any function and its positional or keyword arguments, using *args and **kwargs, and measure average runtime over repetitions with perf_counter.
Learn how parameter defaults are evaluated at function definition time in Python, why mutable defaults cause issues, and how to use None and conditional assignment to ensure fresh values.
Explore the dangers of mutable default arguments in Python, show how None prevents shared state, and demonstrate memoization with a cache dict for factorial.
Explore how Python treats functions as first-class objects, enabling passing, returning, and storing functions; learn about higher-order functions, callables, lambdas, annotations, introspection, and map, filter, reduce, and partials.
Document functions with docstrings and annotations to attach metadata, stored in __annotations__ and dunder doc, enabling external tools like Sphinx to generate documentation.
Explore docstrings and annotations in Python by using function definitions, help, and the doc property to document inputs, outputs, and returns.
Explore lambda expressions as anonymous Python functions, created with the lambda keyword and a single expression body, assignable as function objects, passable as arguments, and comparable to def functions.
Explore lambda expressions and anonymous functions in Python, compare them with def, and learn to pass, apply, and manipulate functions using *args and **kwargs.
Explore how to use Python's sorted with a key function to sort iterables, including numbers, strings, dictionaries, and complex numbers, using lambdas for custom orderings and case-insensitive sorting.
Explore shuffling an iterable with the sorted function by using a random key, importing random and applying random.random in a single line of code.
Explain function introspection by examining function attributes, using the inspect module to reveal names, defaults, code objects, and signatures, and to distinguish functions from methods.
Explore function introspection by defining a Python function, inspecting its annotations, defaults, and docstring, and using inspect to examine code, signature, and parameter kinds.
Explore Python callables: what makes an object callable, how to test with callable(), and how functions, methods, classes, and instances can be invoked with the call operator.
Explore higher-order functions in Python, including map and filter, plus zip, and compare list comprehensions and generator expressions as alternatives to map and filter.
Explore map, filter, zip, and list comprehensions in Python, handling generators, deferred calculations, and combining iterables with two lists, lambdas, and factorial examples.
Explore reducing functions, or accumulators, that fold iterables into a single value using max, min, sum, and custom operations with Python's functools.reduce across sequences, sets, and strings.
Explore reducing functions in Python by building a custom reduce for sequences, then use functools.reduce for any iterable, and apply max, min, sum, any, all, with optional initializer.
Explore how partial functions reduce required arguments by presetting values with def, lambda, or functools.partial, and learn pitfalls with keyword binding and mutable defaults.
Explore Python partials from functools, a higher-order tool that fixes arguments to functions, using lambdas and partials, with examples in sorting and distance-from-origin tasks.
Explore the operator module for functional equivalents of Python operators, using functions like add and mul in reduce, and learn item getter, attribute getter, and method call.
Explore the operator module in Python, using functional equivalents for arithmetic and item/attribute access, including item getter, attribute getter, and method caller for sequences and objects.
Explore local, global, and non-local scopes, including nested scopes, and uncover closures and their distinction from lambda expressions, then examine decorators and the at symbol with practical applications.
Explore global and local scopes, namespaces, and lexical scope in Python. See how module and function scopes bind variables and how the global keyword affects lookups.
Explore global and local scopes in Python, including module behavior, variable shadowing, and the global keyword, with examples using functions, lambdas, and loops.
Explore non-local scopes and nested functions in Python, demonstrating how inner functions access and modify variables in enclosing scopes, use of global and non-local keywords, and scope resolution rules.
Explore non-local scopes in Python by examining how inner functions access and modify variables in outer scopes, using non-local and global declarations across multiple nesting levels.
Explore closures in Python, including free variables, non-local variables, and cells. Learn how closures capture outer scope, how memory cells work, and how nested closures behave.
Explore Python closures, how inner functions capture non-local variables in a cell, and how shared state can be modified across closures, with practical examples.
Explore how closures can replace simple classes to build a running averager with a non-local total and count, and compare timer implementations using a callable closure for elapsed time.
Explore closures that implement counters and track function calls by capturing non-local state. Learn to pass functions, use inner closures, and store call counts in a dictionary.
Explore decorators in Python 3, using closures and higher-order functions to wrap functions, maintain state, and inspect metadata with wraps for proper naming and docs.
Explore how decorators wrap functions with closures to count calls and handle arbitrary parameters. Learn to use wraps to preserve the original function's name, docstring, and signature metadata for inspection.
Explore a timed decorator for Python that profiles function execution using perf counter, prints elapsed time, and compare recursion, looping, and reduce for Fibonacci while discussing memoization and parameterized decorators.
Learn to build a logging decorator in Python, using wraps and UTC time zones, log function calls, and stack decorators to show the impact of call order.
Explore how decorators enable memoization to cache Fibonacci values, reducing computation time, and compare class-based, closure-based, and decorator-based approaches.
Learn how to parameterize decorators in Python by building a decorator factory, using closures and free variables to pass a reps value, and apply it to timing functions.
Learn to create decorator factories that parameterize timing decorators, timing functions with repetitions, and apply both long and short syntax, using closures to pass parameters.
Learn how class instances become callable to decorate functions, implement a decorator factory, and compare closures and dunder call based decorators for function decoration.
Decorate classes with decorators and monkeypatch Python objects at runtime, including adding debug info, customizing fraction class methods like speak and is integral, and applying total ordering.
Develop single dispatch in Python to dispatch by the type of the first argument, replacing overloading, and build a registry driven html formatter with decorators.
Explore implementing a single dispatch generic function in Python using a decorator, a registry, and a type-based dispatch mechanism, including a register decorator and closures.
Explore Python's single dispatch decorator, its registry and dispatch mechanism, and how to safely extend it for sequences, strings, and tuples while avoiding recursion and using closest type matches.
Explore Python tuples as immutable sequence records, distinguish them from lists, and learn how order matters and positions carry meaning. See how named tuples give names to positions for records.
Compare tuples with lists and strings as sequence types, and explore their container nature, immutability, and fixed length; learn how to use tuples for data records and unpacking data.
Explore using tuples as data structures in Python, including indexing, slicing, unpacking (extended with star), immutability nuances, and practical city tuples and function returns.
Learn how named tuples, a class factory that creates tuple subclasses with named fields, improve readability by dot access while preserving tuple immutability and semantics.
Explore named tuples in Python, learn to replace simple classes with immutable, tuple-based data structures, create with collections.namedtuple, access via fields, and compare representation, equality, and practical vector calculations.
Learn how to modify named tuples by creating new instances since they are immutable, using underscore replace for field updates, and extending them with underscore fields to add attributes.
Learn how to modify and extend named tuples without mutating them, using underscore replace and underscore make, and explore unpacking, slicing, and extending fields for practical data structures.
Explore named tuples, their docstrings, and how to set default values with prototype and dunder defaults. Learn to create 2d vectors with origin x and y defaults using underscore replace.
Master named tuples with docstrings and default values, and use a prototype and underscore replace to build vectors with configurable origin coordinates.
Explore using named tuples to return multiple values from a function, illustrated with a random color example featuring red, green, blue, and alpha fields and enhanced PyCharm autocomplete.
Convert dictionaries to named tuples for immutable, readable data access. Learn to create named tuples from dicts, unpack as keyword arguments, and handle missing fields with defaults.
Explore modules, packages, and package namespaces in Python. Learn how Python loads modules and the various import variants, including caching and reloading safety.
Python modules are objects of the module type that live in a namespace. They use global and local dictionaries, and imports populate the global cache and module dict with attributes.
Learn how Python imports modules at runtime, how sys.path and the module cache govern loading, and how Python uses compile and exec to build module namespaces.
Explore how Python imports find and load modules with finders, loaders, and module specs, guided by importlib. See how sys.path and sys.modules shape the namespace and module access.
Explore Python import variants, how they populate globals and sys.modules, and why import star is risky, while understanding export control in modules and packages.
Understand how Python import statements affect namespaces, module loading, and performance, including from module import symbol versus import star, aliasing, and the cost of top-level versus nested imports.
Learn how Python can reload modules, the dangers of manual reload via sys.modules, and why importlib.reload provides a safer in-memory mutation but still not recommended for production use.
Learn how Python uses the __main__ entry point to distinguish running a module from importing it, and build a command-line timing utility with argparse and zipfile.
Review how Python imports modules via import and importlib, how sys.modules caches modules, and how finders, loaders, and path search locate and execute code.
Explore how packages act as specialized modules that can contain modules and subpackages, with dunder path and init file properties, and how Python imports nested packages.
Explore how Python packages and modules are created and imported, including dunder init files and nested packages. Learn how Python resolves paths and dot notation to access modules.
Explore why packages organize Python code into modules and subpackages, separating authentication, authorization, data handling, and tests, and exposing simple imports via __init__.py for easier use.
Explore structuring python packages and imports, nest modules under a common package to create a clean namespace, and control exports with import star and dunder all.
Explore structuring Python packages with nested models, posts, and users, exporting symbols via __init__ and dunder all to flatten namespaces. Learn import strategies and avoid import star.
Explore implicit namespace packages and their difference from regular packages with __init__.py, and read Pep 420 to understand dynamic paths and cross-directory imports.
Zip a package and import it from a zip archive by appending the archive to sys.path, then import common and its nested modules like common.validators.
Explore Python 3.10 changes, including structural pattern matching with match-case, guards and wildcards, and the new zip strict parameter that raises an exception when iterables exhaust unevenly.
Learn Python 3.9’s built-in time zone support with zoneinfo and the IANA time zone database, and discover new gcd, lcm, dictionary union, and remove prefix and suffix features.
Explore the major Python 3.8 changes: positional-only parameters with slash, named arguments via star, and enhanced f-strings with expressions and format specifiers.
Discover Python 3.6 highlights: dictionary order, ordered keyword arguments, f-strings, underscores in literals, and type annotations with tooling implications for mypy and PyCharm.
Preserve insertion order in Python 3.6 dictionaries when iterating keys, values, and items, making them comparable to ordered dicts. Note caveats about 3.6 versus 3.7 official status.
Learn how Python 3.6+ lets underscores separate digits in numeric literals, improving readability for integers, hex, and binary numbers. Follow rules: no prefix, postfix, or double underscores.
Python 3.6 preserves the order of keyword arguments, enabling a defaulted named tuple factory that defines fields and defaults, illustrated by a vector2d with zero origins.
Explore Python 3.6 f-strings, a formatted string literal for string interpolation, using format, positional and keyword arguments, embedded expressions, closures, and practical readability tips.
Hello!
This is Part 1 of a series of courses intended to dive into the inner mechanics and more complicated aspects of Python 3.
This is not a beginner course!
If you've been coding Python for a week or a couple of months, you probably should keep writing Python for a bit longer before tackling this series.
On the other hand, if you've been studying or programming in Python for a while, and are now starting to ask yourself questions such as:
I wonder how this works?
is there another, more pythonic, way, of doing this?
what's a closure? is that the same as a lambda?
I know how to use a decorator someone else wrote, but how does it work? How do I write my own?
why do some boolean expressions not return a boolean value? How can I use that to my advantage?
how does the import mechanism in Python work, and why am I getting side effects?
and similar types of question...
then this course is for you.
To get the most out of this course, you should be prepared to pause the coding videos, and attempt to write code before I do! Sit back during the concept/theory videos, but lean in for the code videos!
Please make sure you review the pre-requisites for this course (below) - although I give a brief refresh of basic concepts at the beginning of the course, those are concepts you should already be very comfortable with as you being this course.
In this course series, I will give you a much more fundamental and deeper understanding of the Python language and the standard library.
Python is called a "batteries-included" language for good reason - there is a ton of functionality in base Python that remains to be explored and studied.
So this course is not about explaining my favorite 3rd party libraries - it's about Python, as a language, and the standard library.
In particular this course is based on the canonical CPython. You will also need Jupyter Notebooks to view the downloadable fully-annotated Python notebooks.
It's about helping you explore Python and answer questions you are asking yourself as you develop more and more with the language.
In Python 3: Deep Dive (Part 1) we will take a much closer look at:
Variables - in particular that they are just symbols pointing to objects in memory (references)
Namespaces and scopes
Python's numeric types
Python boolean type - there's more to a simple or statement than you might think!
Run-time vs compile-time and how that affects function defaults, decorators, importing modules, etc
Functions in general (including lambdas)
Functional programming techniques (such as map, reduce, filter, zip, etc)
Closures
Decorators
Imports, modules and packages
Tuples as data structures
Named tuples
Course Prerequisites
This is an intermediate to advanced Python course.
To have the full benefit of this course you should be comfortable with the basic Python language including:
variables and simple types such as str , bool , int and float types
for and while loops
if...else... statements
using simple lists , tuples , dictionaries and sets
defining functions (using the def statement)
writing simple classes using the class keyword and the __init__ method, writing instance methods, creating basic properties using @property decorators
importing modules from the standard library (e.g. import math)
You should also:
have Python 3.6 (or higher) installed on your system
be able to write and run Python programs using either:
the command line, or
a favorite IDE (such as PyCharm),
have Jupyter Notebooks installed (which I use throughout this course so as to provide you fully annotated Python code samples)