How to pass a class type as a function parameter
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Passing a class type into a function means the function receives the class itself rather than an already-created object. That pattern is useful when the function needs to construct an instance later, choose among implementations, or inspect type metadata.
In Python, Classes Are Ordinary Values
In Python, classes are first-class objects. You can store them in variables, return them from functions, and pass them as arguments just like strings or callables.
Here, EmailSender is the function argument. The function decides when to instantiate it.
Add Type Hints For Clarity
If several related classes are valid, type hints make the contract easier to understand. In modern Python, type[BaseClass] expresses "a class object whose instances are subclasses of this base type."
That annotation helps both readers and static analysis tools. It tells the caller that the function expects a class, not an instance, and that the returned object will conform to the BaseSender interface.
This Pattern Is Common In Factories And Plugins
Class parameters are especially useful in factory-style code. The caller chooses the implementation, while the shared function handles the construction and later workflow.
This keeps the caller in control of the concrete behavior without duplicating the surrounding orchestration logic.
Know When To Pass An Instance Instead
Not every API should accept a class type. If the function needs an object that is already configured and holding state, pass the instance instead.
That function assumes the object already exists. It does not care how the object was created.
If the function needs to control construction time or constructor arguments, then a class parameter makes sense:
This distinction matters because an unclear API forces callers to guess whether they should pass EmailSender or EmailSender("smtp.internal").
The Same Idea Exists In Other Languages
Statically typed languages express the same pattern with different syntax. In Java, a function may accept Class<T>. In C#, the API may accept Type or use a generic type parameter with constructor constraints. The underlying design question is still identical: do you need an already-built object, or do you need the recipe for constructing one?
Once you frame the problem that way, the correct function signature usually becomes obvious.
Common Pitfalls
- Passing an instance when the function really needs the class and constructor behavior.
- Passing a class when the function actually needs a fully configured object.
- Forgetting to document constructor requirements for the accepted classes.
- Omitting base-class or protocol constraints, which makes misuse easier.
- Hiding too much work inside class-based factories so callers cannot tell what gets instantiated.
Summary
- Passing a class type means the function receives the class object itself.
- This is useful for factories, plugins, and delayed construction.
- In Python,
type[BaseClass]is the clearest annotation when subclasses are expected. - Choose between class parameters and instances based on whether the function needs construction control or existing state.

