Python Coding Mistake I Made (And What I Learned)
This week, I was tasked with building a class to handle REST API operations — essentially, implementing basic CRUD (Create, Read, Update…
Python Coding Mistake I Made (And What I Learned)
This week, I was tasked with building a class to handle REST API operations — essentially, implementing basic CRUD (Create, Read, Update, Delete) functionality. I developed the code, committed it to Git, and pushed it for review by my seniors. Here’s a simplified version of what I wrote:
from dataclasses import dataclass
from typing import Optional
@dataclass
class RestConfig:
name: str
date: str
@dataclass
class DepartmentConfig:
dept_name: Optional[str] = "Engineering"
dept_id: Optional[int] = 1
dept_location: Optional[str] = "India"
class RestCRUD:
def create(self, rest_cfg: RestConfig,
dpmt_cfg: DepartmentConfig = DepartmentConfig()):
...
From a design point of view, it made sense to me. I had a default DepartmentConfig, so I instantiated it and passed it as the default value in the create method. I thought: Why pass None and then manually check it inside the function, when I can just give a fully constructed default value right away?
The code went through multiple senior developers. Most of the comments were about refining the operational logic and naming conventions — nothing unexpected.
But then, one senior left a comment that caught me off guard:
“Why not pass None as the default for dpmt_cfg instead of an instance?"
That seemed odd to me. I replied, explaining that since the DepartmentConfig class already had default values, I simply created a default object and passed it directly.
The response I got in return left me enlightened.
He explained that in Python, default argument values are evaluated only once — at the time the function is defined, not every time the function is called. He also shared with me this article.
So, this line:
dpmt_cfg: DepartmentConfig = DepartmentConfig()
This creates a single instance of DepartmentConfig once, when the method is first defined (typically when the module is loaded). Then that same object is reused for every call to create() that doesn't explicitly provide a dpmt_cfg.
If dpmt_cfg is modified inside the method — say, a field is changed or an attribute is added — those changes persist across all future calls to the method. This can lead to confusing bugs and unpredictable behavior.
This issue affects all mutable objects, such as:
- Lists
- Dictionaries
- Sets
- Custom class instances (like DepartmentConfig )
You may not see the issue immediately, especially if the function doesn’t modify the object. But the moment you — or someone else — does, it can cause subtle, hard-to-diagnose bugs in production.
The correct and safe approach is to use None as the default value, and then instantiate the object inside the method if needed:
def create(self, rest_cfg: RestConfig, dpmt_cfg: Optional[DepartmentConfig] = None):
if dpmt_cfg is None:
dpmt_cfg = DepartmentConfig()
...
Why This Works
Using None ensures that a new instance of DepartmentConfig is created every time the method is called without explicitly passing it. This way, each invocation gets its own fresh, independent configuration object. There’s no risk of shared state or accidental data leakage across function calls.
This pattern is known as the “sentinel default” pattern in Python and is the recommended way to handle default arguments when working with mutable types.
This was a small mistake, but it taught me a big lesson:
Never use mutable objects (like lists, dicts, or class instances) as default argument values in Python functions. Always default to None, and initialise inside the function.
It’s a subtle trap, and many developers fall into it — especially when they’re new to Python or coming from other languages where default arguments behave differently.
It looks perfectly reasonable, and it works fine… until one day, it doesn’t.
메타데이터
- post_id
- d9e5093a808f
- slug
- python-coding-mistake-i-made-and-what-i-learned-d9e5093a808f
- url
- https://medium.com/@shras_a/python-coding-mistake-i-made-and-what-i-learned-d9e5093a808f
- canonical_url
- https://medium.com/@shras_a/python-coding-mistake-i-made-and-what-i-learned-d9e5093a808f
- author_url
- https://medium.com/@shras_a
- status
- ok
- fetched_at
- 2026-07-19 11:23:58