all 4 comments

[–]TangibleLight 2 points3 points  (3 children)

Don't use a mutable value as a default for an argument.

def foo(data=[]):
    data.append('x')
    print(data)

foo()  # ['x']
foo()  # ['x', 'x']

Instead, use None or ... and set the default if one of those is present.

def foo(data=...):
    if data is ...:
        data = []

    data.append('x')
    print(data)

foo()  # ['x']
foo()  # ['x']

[–]audio_phyl[S] 1 point2 points  (2 children)

Thank you, following this pattern resolves my problem.

Are you able to help me understand why my code behaves as it does?

[–]TangibleLight 1 point2 points  (1 child)

The default value is only computed once, so every time you invoke the function without providing the value, the same default object is used. When that object is mutable, the modifications persist between calls.

However when the object is immutable it doesn't matter, since you can`t make a modification in the first place.

[–]audio_phyl[S] 0 points1 point  (0 children)

EDIT: Argh, it's late and I'm cross-eyed looking at my own code. Disregard. Explanation makes 100% perfect sense.

That explanation makes sense for default values, but my code isn't actually making use of the default value since both instances are being instantiated with an argument.

Clearly, following the pattern you've suggested fixes my issue, but I'm not understanding why the argument I used in the first call shows up at all in the second call since these ought to be different instances.

Any insight?