This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: namedtuple leaks data between instances when field's default value is empty list
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.10
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Dennis Sweeney, eric.smith, maximus733
Priority: normal Keywords:

Created on 2022-01-21 04:19 by maximus733, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
namedtuple_example.py maximus733, 2022-01-21 04:19 Code to replicate namedtuple with default empty list issue
Messages (4)
msg411079 - (view) Author: (maximus733) Date: 2022-01-21 04:19
Example code to replicate issue on python 3.10.2 is attached.

How to replicate issue:
1. Define a namedtuple where all fields have default values. At least one field's default value will be an empty list: []
2. Instantiate 2 instances of the namedtuple.
3. In the first instance, use namedtuple._replace() to add a replacement list into a field that defaults to [].
4. Now look at the contents of both namedtuples. *Both* of them are modified, even though the code only modified the first object.


***

Output from attached example code:

g.P5 1: myNamedTuple(P1='hello', P2='world', P3=None, P4='', P5=[], P6=[])
h.P5 1: myNamedTuple(P1='good', P2='morning', P3=None, P4='', P5=[], P6=[])
Expected: g.P5: []
Actual:   g.P5: []
Expected: h.P5: []
Actual:   h.P5: []
g.P5 2: myNamedTuple(P1='hello', P2='world', P3=None, P4='', P5=['a', 'b'], P6=[])
h.P5 2: myNamedTuple(P1='good', P2='morning', P3=None, P4='', P5=['a', 'b'], P6=[])
Expected: g.P5: ['a', 'b']
Actual:   g.P5: ['a', 'b']
Expected: h.P5: []
Actual:   h.P5: ['a', 'b']
g.P5 3: myNamedTuple(P1='hello', P2='world', P3=None, P4='', P5=['a', 'b', 'c', 'd'], P6=[])
h.P5 3: myNamedTuple(P1='good', P2='morning', P3=None, P4='', P5=['a', 'b', 'c', 'd'], P6=[])
Expected: g.P5: ['a', 'b', 'c', 'd']
Actual:   g.P5: ['a', 'b', 'c', 'd']
Expected: h.P5: []
Actual:   h.P5: ['a', 'b', 'c', 'd']
msg411080 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python committer) Date: 2022-01-21 04:46
This seems to be the standard confusion people have with mutable defaults, just with namedtuple defaults rather than function defaults.

Similar behavior elsewhere in Python:

>>> def f(x, y=[]):
...     y.append(x)
...     print(y)
... 
...     
>>> f(1)
[1]
>>> f(2)
[1, 2]
>>> f(15)
[1, 2, 15]

Or even

>>> a = []
>>> b = a
>>> b.append(4)
>>> a
[4]

This happens because the values you provide as defaults are *objects*, not *expressions* to be re-evaluated. If you make one of the defaults a list, you ask the namedtuple to "give me this particular list every time", and it will give you that same list, even if it has been modified.

There is currently no support for "construct a brand new list every time" in namedtuples. You could look to the dataclasses module for such a feature, where you can specify default_factory=list, and it will make a new list for each instance.

https://docs.python.org/3/library/dataclasses.html#dataclasses.field
msg411103 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2022-01-21 09:41
Also see https://docs.python.org/3/faq/programming.html#why-are-default-values-shared-between-objects
msg411132 - (view) Author: (maximus733) Date: 2022-01-21 15:09
Thanks for the clarification.
History
Date User Action Args
2022-04-11 14:59:55adminsetgithub: 90608
2022-01-21 15:09:01maximus733setmessages: + msg411132
2022-01-21 09:41:55eric.smithsetstatus: open -> closed

nosy: + eric.smith
messages: + msg411103

resolution: not a bug
stage: resolved
2022-01-21 04:46:58Dennis Sweeneysetnosy: + Dennis Sweeney
messages: + msg411080
2022-01-21 04:19:36maximus733create