This rule raises an issue when a mutable object is used as the default value in dict.fromkeys() or contextvars.ContextVar().

Why is this an issue?

In Python, when you pass a mutable object as a default value to certain constructs, all instances share the same object reference rather than getting independent copies. This creates unintended shared state that leads to subtle mutation bugs.

Python evaluates the default value expression once when the code is defined, not each time it is used. The same object instance is then reused across all keys or contexts. This is different from creating new instances for each use.

This issue is related to, but distinct from, the more commonly known problem of mutable default arguments in function definitions (e.g., def func(items=[])), which has the same underlying cause.

The problem with dict.fromkeys()

The dict.fromkeys(keys, value) method creates a dictionary with all specified keys pointing to the same value object. When this value is mutable (like a list or dictionary), all keys share the same object reference:

keys = ['a', 'b', 'c']
my_dict = dict.fromkeys(keys, [])
my_dict['a'].append(1)
print(my_dict)  # {'a': [1], 'b': [1], 'c': [1]}

Modifying the list through one key affects all other keys because they all reference the same list object in memory.

The problem with ContextVar()

Similarly, when you create a ContextVar with a mutable default value, that same object is shared across all contexts:

from contextvars import ContextVar

my_var = ContextVar('my_var', default=[])
my_var.get().append(1)  # Modifies the shared default

This is particularly problematic in concurrent code where different contexts should have independent state.

What is the potential impact?

Using mutable default values in these contexts can cause serious bugs:

How to fix it

For dict.fromkeys(), replace it with a dictionary comprehension that creates a new instance of the mutable object for each key. For ContextVar(), use a factory pattern or set the default in context initialization rather than passing a mutable default directly.

Code examples

Noncompliant code example

keys = ['a', 'b', 'c']
my_dict = dict.fromkeys(keys, [])  # Noncompliant

Compliant solution

keys = ['a', 'b', 'c']
my_dict = {key: [] for key in keys}

Noncompliant code example

from contextvars import ContextVar

my_var = ContextVar('my_var', default=[])  # Noncompliant

Compliant solution

from contextvars import ContextVar

my_var: ContextVar[list] = ContextVar('my_var')

def get_my_var() -> list:
    try:
        return my_var.get()
    except LookupError:
        value: list = []
        my_var.set(value)
        return value

Resources

Documentation

External coding guidelines

Related rules