Key Points

Introduction


  • This workshop will cover a variety of intermediate Python topics that are commonly encountered in programming but may not be covered in beginner courses.

Virtual Environments


  • Setting up a virtual environment is useful for managing project dependencies.
  • Using uv simplifies the process of creating and managing virtual environments.
  • There are several options other than uv for managing virtual environments, such as venv and conda.
  • It’s important to version control your project from the start, including a .gitignore file.

Python Bits and Bobs


Iterables and Generators


  • An iterable is any Python object that implements the __iter__() method, which returns an iterator.
  • Iterators also implement the __next__() method, which returns the next item in the sequence when called.
  • A generator is a special type of iterable that allows you to generate values on the fly, rather than storing them all in memory at once.
  • Sets are unordered collections of unique elements that support mathematical set operations like union, intersection, difference, and symmetric difference.

Collections & Iterables


  • The collections module provides useful functions and objects for working with data more efficiently, such as Counter, deque, and defaultdict.
  • The itertools module provides useful functions for working with iterables, such as combinations, permutations, chain, and cycle.

The Logging Module


  • The logging module is useful for tracking the behavior of our program.
  • Using logging levels makes it easy to filter important messages from less important ones.
  • Custom logging formats can provide more context and make logs easier to understand.

Decorators & Caching


  • The functools module provides useful tools for working with functions, such as cache, lru_cache, singledispatch, and wraps.
  • Caching can speed up repeated function calls by storing results in memory, but it can also consume memory if not used carefully.
  • The singledispatch decorator allows us to define different behaviors for a function based on the type of its input.
  • The wraps decorator helps preserve the original function’s metadata when creating decorators.

Creating A Module


  • Python modules are simply directories with an __init__.py file in them
  • You can add the path to your module directory to sys.path to make it available for import
  • You can use dot notation in your imports to specify the module, file, and function you want to use

Class Objects


  • Python classes are defined using the class keyword, followed by the class name and a colon.
  • The __init__ method is a special method that is called when an instance of the class is created.
  • Class methods are defined like normal functions, but they must include self as the first parameter.

More On Class Objects


  • Dunder methods are special methods that start and end with double underscores.
  • Static properties and methods are defined on the class itself, rather than on instances of the class.
  • We can use the @property decorator to define properties that can be accessed like attributes.
  • We can use the @classmethod decorator to define methods that operate on the class itself, rather than on instances of the class.
  • We can use the @staticmethod decorator to define methods that don’t operate on either the class or instances of the class, but are still related to the class in some way.

Extending Classes with Inheritance


  • Inheritance allows us to create a new class that is a specialized version of an existing class
  • We can override methods and properties in a subclass to provide specialized behavior

Unit Testing


  • We can use pytest to write and run unit tests in Python.
  • A good test is isolated, repeatable, fast, and clear.
  • We can use fixtures to provide data or state to our tests.
  • We can use monkey patching to modify the behavior of functions or classes during testing.
  • Test Driven Development (TDD) is a practice where we write tests before writing the code to make the tests pass.

Inheritance and Composition


  • Composition allows us to build complex functionality by combining several smaller, simpler classes
  • Composition promotes separation of concerns, reusability, flexibility, and maintainability
  • By using abstract base classes, we can define interfaces that subclasses must implement, allowing for flexibility in our code design

Static Code Analysis


  • There are many static code analysis tools available for Python, each with its own strengths and weaknesses.
  • Ruff is a fast linter and code formatter that can replace several other tools, including flake8, pylint, and isort.
  • MyPy is a static type checker that can help catch type-related errors in Python code.

Building and Deploying a Package


  • We can build our package locally using uv build, which creates a dist/ directory with the built package files.
  • We can use GitHub Actions to automate the building and deployment of our package.
  • GitHub Actions workflows are defined using YAML files in the .github/workflows/ directory.
  • We can trigger our workflow to run on specific events, such as pushing a tag that follows semantic versioning.
  • We can add additional steps to our workflow, such as running tests and static code analysis, to ensure code quality before deployment.

Multiprocessing


  • Multiprocessing allows us to run multiple processes concurrently, which can improve performance for CPU-bound tasks.