Why keras use call instead of __call__?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Keras layers use a call() method instead of __call__() because the base Layer class defines __call__() to handle essential bookkeeping — building the layer on first use, tracking input shapes, managing training vs inference mode, applying regularization, and recording the computation graph. Your custom call() method contains only the forward-pass logic, while __call__() wraps it with all the framework plumbing. This separation keeps custom layer code clean and ensures consistent behavior across all layers.
How It Works Internally
When you write output = my_layer(input), Python calls my_layer.__call__(input). Inside Layer.__call__():
You only implement step 4. Steps 1-3 and 5-6 are handled automatically.
Writing a Custom Layer
What call Does That call Does Not
| Responsibility | Handled by __call__ | Your call |
| Build weights on first use | Yes | No |
| Input shape validation | Yes | No |
| Dtype casting | Yes | No |
| Training/inference flag | Yes (passed through) | Receives it |
| Activity regularization | Yes | No |
| Graph connectivity tracking | Yes | No |
| Eager vs graph execution | Yes | No |
| Masking support | Yes | No |
The training Argument
__call__ passes the training flag to your call method:
You do not need to handle the training flag plumbing — __call__ manages it.
Comparison with PyTorch
PyTorch uses the same pattern but with forward() instead of call():
Both frameworks use the same design: __call__ handles framework logic, and users override only the forward pass method.
Why Not Override call Directly
Custom Layer with Multiple Inputs
Common Pitfalls
- Overriding
__call__instead ofcall: Overriding__call__bypasses weight building, input validation, regularization, and graph tracking. The layer may appear to work in simple tests but fails during model saving, distributed training, or when used withmodel.fit(). - Creating weights inside
callinstead ofbuild: Weights created incallare recreated on every forward pass. Create them inbuild()(called once) and use them incall(). Theadd_weight()method registers them for training and serialization. - Calling
self.call()directly instead ofself(): Callingself.call(inputs)skips the__call__bookkeeping. If a layer internally uses another layer, call it asself.sub_layer(inputs), notself.sub_layer.call(inputs). - Forgetting to call
super().__init__(**kwargs)in__init__: Without calling the parent constructor, the layer is not properly registered. Features likelayer.name,layer.dtype, andlayer.trainablewill not work. - Not accepting
**kwargsin__init__: Keras passes configuration arguments (likenameanddtype) through**kwargs. Without accepting them,MyLayer(name="my_layer")raises aTypeError.
Summary
- Keras uses
call()so that__call__()can handle weight building, dtype casting, graph tracking, and regularization - Override
call()for forward pass logic,build()for weight creation - Never override
__call__()— it breaks the framework's internals - PyTorch uses the same pattern with
forward()instead ofcall() - Always call layers as
layer(inputs), notlayer.call(inputs), to trigger the full lifecycle

