Skip to content

Latest commit

 

History

History
148 lines (97 loc) · 15.2 KB

File metadata and controls

148 lines (97 loc) · 15.2 KB

This project has been created as part of the 42 curriculum by Edson Baptista Finda.

python

The exercise-by-exercise journey — problem statements, my reasoning, and the concepts each one was designed to teach — is fully documented on Notion. This README is a lighter, GitHub-native companion to that: a concept-first map of what each module covers and which exercises apply it, so the code is legible on its own without needing the full write-up open.

Contents

Description

This repository holds my solutions to a Python Piscine: eleven modules (module00 through module10) that go from the language's basics to writing idiomatic, modern Python (3.10–3.14) across OOP, packaging, external libraries, and functional-style code. Each module wraps its exercises in a small theme — a garden, an alchemy grimoire, a monster-battling RPG, the Matrix — but the theming is a skin over a specific concept each exercise is built to force.

Every exercise lives in its own exN/ folder as a runnable, self-contained script. The sections below aren't a file listing — they walk through the concept each module targets and point to the exercises that apply it. For the deeper narrative behind why a given approach was chosen over another, that's what the Notion documentation is for.

module00 — Fundamentals

#module00--fundamentals

The warm-up: functions, input()/f-strings, type casting, conditionals, loops, and a first look at recursion — before any of it is wrapped in a class.

  • ex0ex3: A function per exercise (ft_hello_garden, ft_garden_name, ft_plot_area, ft_harvest_total), moving from a static print to reading and casting user input (int(input(...))) and formatting output with f-strings.
  • ex4/ex5: if/else branching on user input (ft_plant_age, ft_water_reminder).
  • ex6: The same problem solved two ways — ft_count_harvest_iterative with a for loop, ft_count_harvest_recursive with a nested helper function and default parameter values, comparing iteration against recursion for the same task.
  • ex7: ft_seed_inventory takes multiple typed parameters (with type hints) and dispatches on one of them, plus string methods like .capitalize().

module01 — OOP basics: classes, encapsulation & inheritance

#module01--oop-basics-classes-encapsulation--inheritance

The first real class, then everything that follows from it: private-by-convention attributes, classmethod/staticmethod, and single and multi-level inheritance.

  • ex1Plant: The core class. Attributes prefixed with _ (Python's encapsulation convention), constructor-level validation, getters/setters that reject bad state, a @classmethod factory (make_anonymous) that builds a Plant without normal arguments, a @staticmethod (is_older_than_1y) that doesn't need an instance at all, and a nested class (Stats) tracking how many times each method has been called.
  • ex2/ex3: Importing Plant across files and using it in a loop (ft_plant_growth) and in a batch of instances (ft_plant_factory).
  • ex4: ft_garden_security — proving the encapsulation from ex1 actually holds by trying (and failing) to force a Plant into an invalid state.
  • ex5Flower/Tree/Vegetable: Single inheritance from Plant, each subclass calling super().__init__() and overriding show() while adding its own behavior and state.
  • ex6Seed: A second level of inheritance (Seed → Flower → Plant), extending inherited state (self._stats.insert(...)) rather than replacing it.

module02 — Exception handling

#module02--exception-handling

try/except/finally used for real control flow, escalating from catching a single built-in exception to a small custom exception hierarchy.

  • ex0: A first try/except ValueError.
  • ex1TempError: A custom exception with __str__ overridden for a readable message, caught alongside a built-in one via a tuple: except (ValueError, TempError).
  • ex2: Deliberately triggering four different built-in exceptions (ValueError, ZeroDivisionError, FileNotFoundError, TypeError) from one dispatch function to practice catching each by type.
  • ex3GardenError / PlantError / WaterError: A small exception hierarchy — two specific exceptions inheriting from a common base — caught either individually or, just as validly, by catching the shared base class.
  • ex4: *args for a variadic function, and a finally block that guarantees cleanup ("closing watering system") runs whether or not an exception was raised.

module03 — CLI arguments & core data structures

#module03--cli-arguments--core-data-structures

Reading real input from sys.argv, then a tour of Python's core containers — tuples, sets, dicts — plus generators and comprehensions.

  • ex0/ex1: sys.argv, sys.exit, parsing CLI arguments into a list of numbers and computing aggregates (sum, min, max).
  • ex2: Tuple unpacking (x, y, z = ...), Python's structural pattern matching (match/case) to distinguish KeyboardInterrupt from EOFError, and the distinction between catching Exception vs the broader BaseException.
  • ex3: set operations — union (|), intersection (&), difference (-) — used to compare which achievements different players share or are missing.
  • ex4: dict construction from parsed input, dict.update(), and computing per-key percentages of a whole.
  • ex5: Generators — an infinite generator (while True: yield) for random events, and a second generator that mutates the list it's consuming from (events.pop(index)) as it's iterated with next().
  • ex6: List and dict comprehensions, including a comprehension with a filtering condition.

module04 — File I/O & streams

#module04--file-io--streams

Opening, reading, writing, and safely closing files — ending in the idiomatic way to do all of it.

  • ex0: open()/.read()/.close(), with OSError caught around the open() call.
  • ex1: A reusable ft_safe_open() helper, the walrus operator (if file := ft_safe_open(path):) to open and check in one expression, iterating a file line by line, and writing transformed content to a new file.
  • ex2: Doing the same job with sys.stdin.readline() / sys.stdout.write() / sys.stdout.flush() instead of input()/print(), to work directly with the stream objects.
  • ex3: The context manager form, with open(file_name, mode=action) as file:, which guarantees the file is closed even if an exception is raised inside the block — plus returning a tuple[bool, str] as a functional alternative to raising an exception for an expected failure case.

module05 — Abstraction: ABCs, protocols & dunder methods

#module05--abstraction-abcs-protocols--dunder-methods

Where Python's abstraction tools stop being theoretical: an abstract base class enforced at runtime, then a second way to achieve the same polymorphism without inheritance at all.

  • ex0DataProcessor(ABC): abc.ABC and @abstractmethod force NumericProcessor, TextProcessor, and LogProcessor to each implement validate()/ingest(), while sharing the base's output() behavior.
  • ex1DataStream: Dunder methods (__len__, __str__) that make custom objects work with len() and print() naturally, and compositionDataStream holds a set of DataProcessor instances and dispatches to whichever one validates a given item, all through the shared abstract interface.
  • ex2ExportPlugin(Protocol): typing.Protocol for structural typingCSVExportPlugin and JSONExportPlugin satisfy the plugin interface just by having a matching process_output() method, with no inheritance from ExportPlugin required at all. The clearest contrast in the repo between nominal typing (ex0's ABC) and structural typing (Protocol).

module06 — Modules & packages

#module06--modules--packages

Not language syntax so much as Python's import system: the difference between a module and a package, and exactly what __init__.py does and doesn't expose.

  • ft_alembic_0ft_alembic_5: import x vs from x import y, first against a single-file module (elements.py), then against a package (alchemy/) — and what changes once alchemy/__init__.py re-exports specific names (ft_alembic_4 deliberately tries to reach a function __init__.py doesn't re-export, to show it fails).
  • ft_distillation_0/1: Reaching a function directly through its submodule vs through the package's re-exported (and aliased — heal = healing_potion) name.
  • ft_kaboom_0/1: alchemy/grimoire/__init__.py only re-exports light_spellbook, not dark_spellbookft_kaboom_1 imports dark_spellbook directly to show that submodules not surfaced by __init__.py are still reachable by their full dotted path, they're just not part of the package's public surface.
  • ft_transmutation_02: A subpackage (alchemy/transmutation/) reached three different ways — direct submodule import, subpackage import, and through the top-level package — to make the dotted-path resolution rules concrete.

module07 — Design patterns in Python OOP

#module07--design-patterns-in-python-oop

Classic OOP design patterns, implemented Python-style — including the multiple inheritance Python allows that C++-style single-inheritance languages don't make as easy.

  • ex0Creature / CreatureFactory: An Abstract FactoryFlameFactory/AquaFactory each produce a matching base/evolved pair (FlamelingPyrodon, AquabubTorragon) without the caller needing to know the concrete classes.
  • ex1HealCapability / TransformCapability: Multiple inheritance as mixinsSproutling(Creature, HealCapability) combines two unrelated abstract bases, each contributing its own behavior, with both __init__s called explicitly where needed.
  • ex2BattleStrategy: The Strategy patternNormalStrategy/AggressiveStrategy/DefensiveStrategy each implement act() differently, using isinstance() checks against the ex1 mixins (and typing.cast) to validate that a given creature actually supports the strategy being applied to it.

module08 — Environments & dependency management

#module08--environments--dependency-management

Python outside the language itself: isolating a project's dependencies, managing them, and configuring an app without hardcoding secrets.

  • ex0construct.py: Detecting whether the script is running inside a virtual environment by comparing sys.prefix to sys.base_prefix, and locating the environment's package install path via site.getsitepackages().
  • ex1loading.py: importlib.import_module() for checking optional/third-party dependencies (numpy, pandas, matplotlib, requests) at runtime with a friendly error instead of a crash, alongside both dependency-management styles side by side — requirements.txt (pip) and pyproject.toml (Poetry).
  • ex2oracle.py: Loading configuration from a .env file via python-dotenv, validating that required variables are present and well-formed, and masking secrets in output depending on whether MATRIX_MODE is development or production.

module09 — Data validation with Pydantic

#module09--data-validation-with-pydantic

Runtime, type-safe data validation with Pydantic — moving field validation out of scattered if checks and into declarative model definitions.

  • ex0SpaceStation: A pydantic.BaseModel with per-field constraints (Field(ge=..., le=..., min_length=..., max_length=...)), and an invalid instantiation caught as a ValidationError with a structured, field-level error message.
  • ex1AlienContact: A StrEnum for a constrained set of valid values, automatic type coercion (passing '8.5' into a float field), and @model_validator(mode="after") for cross-field business rules a single Field constraint can't express (e.g. physical contact reports must be verified; strong signals require a received message), raised as a PydanticCustomError.
  • ex2SpaceMission / CrewMember: Nested Pydantic models — a SpaceMission holding a list[CrewMember], each validated independently — plus a model_validator enforcing rules across the whole nested structure (a mission needs at least one Commander/Captain, and long missions need enough experienced crew).

module10 — Functional programming

#module10--functional-programming

Python's functional side: lambdas, closures, functools, and decorators, each building on the last.

  • ex0: lambda as the key= argument to sorted/max/min, and with filter/map.
  • ex1: Higher-order functions — functions that take other functions as arguments and/or return new functions (spell_combiner, power_amplifier, conditional_caster), typed with Callable type aliases.
  • ex2: Closuresmage_counter, spell_accumulator, and memory_vault each return an inner function (or dict of them) that keeps its own private state alive between calls via the nonlocal keyword, with no class involved.
  • ex3: functools.reduce, functools.partial (pre-filling arguments), @functools.lru_cache (memoizing a recursive Fibonacci), and @functools.singledispatch (dispatching a function's behavior based on its argument's runtime type).
  • ex4: Decorators — a plain decorator (@spell_timer), a parameterized decorator factory (@power_validator(10), @retry_spell(3)), functools.wraps to preserve the wrapped function's __name__/metadata, and a decorator applied to a function defined inside a class method.