{"id":2291,"date":"2025-09-22T11:24:58","date_gmt":"2025-09-22T11:24:58","guid":{"rendered":"https:\/\/www.cmarix.com\/qanda\/?p=2291"},"modified":"2026-02-05T11:59:12","modified_gmt":"2026-02-05T11:59:12","slug":"mutable-default-arguments-bug-in-python","status":"publish","type":"post","link":"https:\/\/www.cmarix.com\/qanda\/mutable-default-arguments-bug-in-python\/","title":{"rendered":"Why does Mutable Default Argument in a Python Class or Function Cause Bugs over time?"},"content":{"rendered":"\n<p>If you\u2019ve ever seen a function or class in Python behave as if it\u2019s \u201cremembering\u201d values from earlier calls when it shouldn\u2019t, you\u2019ve likely run into this issue.<\/p>\n\n\n\n<p>Let\u2019s break down what\u2019s happening, why it\u2019s a problem, and how to fix it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Bug: Unexpected Shared State Between Function Calls or Class Instances<\/h2>\n\n\n\n<p>You may have seen bugs where lists or dictionaries inside a function or class seem to \u201cremember\u201d data from previous calls \u2014 even when you expected them to be empty.<\/p>\n\n\n\n<p><strong>Take this example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def collect_items(item, items=&#91;]):\n    items.append(item)\n    return items\n\nprint(collect_items(\"apple\"))   # &#91;'apple']\nprint(collect_items(\"banana\"))  # &#91;'apple', 'banana'] \u2190 Wait, what?<\/code><\/pre>\n\n\n\n<p><strong>Or:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class Tracker:\n    def __init__(self, logs=&#91;]):\n        self.logs = logs\n\na = Tracker()\na.logs.append(\"start\")\n\nb = Tracker()\nprint(b.logs)  # &#91;'start'] \u2190 Unexpected!<\/code><\/pre>\n\n\n\n<p>What\u2019s going on? Each call or instance seems to be a sharing state \u2014 which shouldn\u2019t happen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Root Cause: Default Argument Values Are Evaluated Once<\/h2>\n\n\n\n<p>In Python, default argument values are evaluated <strong>only once<\/strong>, when the function or class is defined \u2014 <strong>not each time it\u2019s called<\/strong>. If that default is a mutable object (like a list or dictionary), it persists across calls or object instances.<\/p>\n\n\n\n<p>So when you modify that object (e.g. items.append(&#8230;)), it modifies the <strong>same list in memory<\/strong> every time.<\/p>\n\n\n\n<p>This is one of the most infamous &#8220;gotchas&#8221; in Python \u2014 especially in beginner-to-intermediate level code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to Fix It: Using Safe Patterns for Default Arguments<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Never Use Mutable Types as Default Arguments<\/h3>\n\n\n\n<p>Instead, use None and initialize the object inside the function or method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def collect_items(item, items=None):\n    if items is None:\n        items = &#91;]\n    items.append(item)\n    return items<\/code><\/pre>\n\n\n\n<p>This ensures that a new list is created each time you call the function.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Apply the Same Pattern in Classes<\/h3>\n\n\n\n<p><strong>Fix your class constructor like this:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class Tracker:\n    def __init__(self, logs=None):\n        if logs is None:\n            logs = &#91;]\n        self.logs = logs<\/code><\/pre>\n\n\n\n<p>Now each instance of Tracker() has its own independent list.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Be Cautious When Using Mutable Defaults in Third-Party Code<\/h3>\n\n\n\n<p>If you\u2019re using a library and noticing strange shared behavior, inspect the source (if possible) or check the documentation. They might be using mutable default arguments internally.<\/p>\n\n\n\n<p>As a workaround, you can explicitly pass your own clean list or dict as an argument if supported.<\/p>\n\n\n\n<p>Mutable default arguments in Python can lead to shared state across function calls or class instances \u2014 causing bugs that are hard to trace. This happens because the default object is only created once during function definition.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Best Practices:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Always use None as the default value when expecting a list, dict, or set<\/li>\n\n\n\n<li>Create a new mutable object inside the function or method<\/li>\n\n\n\n<li>Apply this pattern consistently in both functions and class constructors<\/li>\n\n\n\n<li>Be mindful of mutable defaults in third-party libraries<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>This issue can be subtle but dangerous. Shared state across calls or instances can lead to confusing bugs and unexpected behavior. Understanding how Python handles default arguments is an important step in writing clean, reliable code. If you&#8217;re building something complex or reviewing shared-state bugs, it&#8217;s a smart move to <a href=\"https:\/\/www.cmarix.com\/hire-python-developers.html\">hire Python developers<\/a> who are familiar with these kinds of edge cases and can write safer, more predictable code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you\u2019ve ever seen a function or class in Python behave as if it\u2019s \u201cremembering\u201d values from earlier calls when it shouldn\u2019t, you\u2019ve likely run into this issue. Let\u2019s break down what\u2019s happening, why it\u2019s a problem, and how to fix it. The Bug: Unexpected Shared State Between Function Calls or Class Instances You may [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":2294,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[163,3],"tags":[],"class_list":["post-2291","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","category-web"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/2291","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/comments?post=2291"}],"version-history":[{"count":5,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/2291\/revisions"}],"predecessor-version":[{"id":2298,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/2291\/revisions\/2298"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media\/2294"}],"wp:attachment":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media?parent=2291"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/categories?post=2291"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/tags?post=2291"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}