The ‘Shared List’ Trap in Python Class Attributes: Why Your Friend Became Everyone’s Friend?

Today, I want to share a very common pitfall in Python—strange behavior when the default value of a class attribute is a mutable object (like a list).

A seemingly simple example

Let’s first look at a simple <span>Person</span> class definition:

class Person:
    def __init__(self, name:str, frients:list=[]):
        self.name = name
        self.frients = frients

    def show_frients(self):
        print(f"{self.name}, frients: {self.frients}")

It looks normal, right? When we create a <span>Person</span> instance without providing the <span>frients</span> parameter, it defaults to an empty list.

The strange phenomenon appears

Let’s create two <span>Person</span> instances:

def main():
    zhangsan = Person("zhangsan")
    lisi = Person("lizi")
    zhangsan.frients.append("x")
    
    zhangsan.show_frients()
    lisi.show_frients()

The output will surprise you:

zhangsan, frients: ['x']
lisi, frients: ['x']

What? We only added friend “x” for Zhang San, why does Li Si also automatically have this friend?

Why does this happen?

This phenomenon seems strange, but there is a reasonable explanation:

  1. Default parameters are evaluated at function definition time: Python’s function default parameters are created at the time of function definition (not at call time)
  2. All instances share the same default list: Since the <span>frients</span> parameter was not explicitly passed, both instances use the same default list object
  3. Modifying the same list: When one instance modifies the list, all instances using the default list will be affected

How to avoid this pitfall?

Method 1: Explicitly pass an empty list

def main():
    zhangsan = Person("zhangsan", [])
    lisi = Person("lizi", [])
    zhangsan.frients.append("x")
    zhangsan.show_frients()
    lisi.show_frients()

This method works, but you have to write an <span>[]</span> every time you create an instance, which defeats the purpose of a default parameter.

Method 2: Modify the class definition (recommended)

A better approach is to avoid this issue in the class definition:

class Person:
    def __init__(self, name:str, frients:list|None=None):
        self.name = name
        if frients is None:
            self.frients = []
        else:
            self.frients = frients

In this way:

  • The default parameter uses <span>None</span> (an immutable object)
  • A new list is created based on the situation in <span>__init__</span>
  • Each instance will get its own independent list

PyCharm’s helpful reminder

If you use PyCharm, it will intelligently identify this potential issue and give a warning:

The 'Shared List' Trap in Python Class Attributes: Why Your Friend Became Everyone's Friend?

Accepting its suggestion will automatically fix this issue.

Conclusion

This “trap” actually reflects a design choice in Python: default parameters are evaluated at function definition time. This is not a problem for immutable objects (like numbers, strings, tuples), but it can lead to unexpected behavior for mutable objects (like lists, dictionaries).

Remember this principle: Never use mutable objects as default parameters for functions/methods, use <span>None</span> instead and initialize inside the function.

I hope this article helps you avoid this common pitfall for Python beginners!

Leave a Comment