Frameworks like Spring, NestJS, and FastAPI wire your objects together for you: you declare what a class needs in its constructor, and the framework figures out how to build those dependencies and pass them in. That mechanism is a dependency injection (DI) container. You will build a small one.
Implement Container:
register(self, interface: type, implementation: type, singleton: bool = False) -> None — bind an interface (or any type) to a concrete implementation. When something asks for interface, the container should build implementation. If singleton is True, the same instance is reused on every resolve.resolve(self, cls: type) — construct an instance of cls. Inspect cls.__init__'s parameters and their type annotations; for each annotated parameter, look up its binding (falling back to the annotated type itself if there is no binding) and resolve it recursively, then call cls with the resolved dependencies.Unregistered dependencies pass through: if a parameter's type has no binding, resolve that concrete type directly (assume it is constructible via the same rules). Parameters with no type annotation are skipped.
Example:
Car.__init__(self, engine: Engine, logger: ILogger)Aftercontainer.register(ILogger, ConsoleLogger), callingcontainer.resolve(Car)builds aConsoleLogger(bound), builds anEngine(unregistered → pass-through), and returnsCar(engine=<Engine>, logger=<ConsoleLogger>).
Constraints:
inspect to read constructor parameters and annotationssingleton=True bindings return an identical instance across resolves; singleton=False returns a fresh instance each timeOfficial solution locked
Give the problem a real attempt before peeking — revealing it affects your credit for this problem for 4 hours.