-2

I was reading this very informative question and answer and learned about this behavior for the first time: calling

def foo(l=[]):
    l.append(1)
    print(l)

foo()
foo()
foo([])
foo()

prints

[1]
[1,1]
[1]
[1,1,1]

I thought that was neat and wanted to try it with other variable types as default arguments.

Running

import math
def foo(l=[],bar=0,baz={"z":0},bap="a"):
    l.append(1)
    bar+=1
    baz["z"]+=1
    bap=chr(ord(bap)+1)
    print(locals())

foo()
foo()
foo([],math.pi,{"z":0},"?")
foo()

prints

{'l': [1], 'bar': 1, 'baz': {'z': 1}, 'bap': 'b'}
{'l': [1, 1], 'bar': 1, 'baz': {'z': 2}, 'bap': 'b'}
{'l': [1], 'bar': 4.141592653589793, 'baz': {'z': 1}, 'bap': '@'}
{'l': [1, 1, 1], 'bar': 1, 'baz': {'z': 3}, 'bap': 'b'}

which caught me totally off-guard. I was expecting incrementing the integer bar and string bap to be analogous to appending/modifying elements of l and baz and cause similar behavior, but it didn't - they print the same values each foo call (unless non-default arguments are provided).

Hence the question in the title. I was thinking that the difference was caused by iterable versus non-iterable data types in my example.

DoodleVib
  • 159
  • 13
  • It is mutable vs immutable default arguments. Check also https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments – buran Oct 15 '22 at 07:10

1 Answers1

0

I learned from another informative post and a helpful link therein that my suspicion was off-target. The issue isn't caused by iterable versus non-iterable types.

The difference in default argument behavior happens because some of those default arguments I chose are mutable (l list, baz dict) and others are immutable (bar int, bap str).

DoodleVib
  • 159
  • 13
  • 1
    So, you post question in order to answer it yourself? Both asked and answered 10 minutes ago... This will be closed as dupe in no time. – buran Oct 15 '22 at 07:12
  • @buran, I'm not trying to "dupe" anyone. I sincerely believed iterable and non-iterable data types were the cause of this behavior, couldn't find a satisfactory answer about iterable arguments in docs or SO, spent an hour researching Python iterability and writing my question, then stumbled on this [post](https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument) by chance. I hope that someone else who tries to understand this behavior by researching iterability and has no clue about mutable arguments will avoid the same dead-end that I was going down. – DoodleVib Oct 15 '22 at 17:14
  • ["dupe", short/informal for "duplicate"](https://en.wiktionary.org/wiki/dupe#Etymology_2), as it happen – buran Oct 15 '22 at 17:16
  • Thank you for clarifying that you meant "dupe" as in "duplicate," not "dupe" as in "cruel trick." It seems that the thrust of the question would be more clear with "iterable" in the title, so I'll make that modification. The SO blog alleged ["it is not merely OK to ask and answer your own question, it is *explicitly encouraged*"](https://stackoverflow.blog/2011/07/01/its-ok-to-ask-and-answer-your-own-questions/), and encountering a question with this explanation of the behavior would have saved me over an hour. – DoodleVib Oct 15 '22 at 17:18
  • By the way, one more reference https://docs.python.org/3/faq/programming.html?highlight=functions#why-are-default-values-shared-between-objects _By definition, immutable objects such as numbers, strings, tuples, and None, are safe from change. Changes to mutable objects such as dictionaries, lists, and class instances can lead to confusion._ – buran Oct 15 '22 at 17:25