Python default arguments oddities | Sololearn: Learn to code for FREE!
New course! Every coder should learn Generative AI!
Try a free lesson
+ 6

Python default arguments oddities

Hi all, on the basis that default arguments, when mutable, retain their values across successive function calls (this is not applicable to immutable default arguments), I can understand Oddity Number One (sorry, I'm making reference to code below). But I am not able to explain the result in Oddity Number Two. Can anyone enlighten that? https://code.sololearn.com/cnr9OmZ4DK6M

20th Jan 2020, 2:24 PM
Bilbo Baggins
Bilbo Baggins - avatar
11 Answers
+ 4
Hm, so if you do f2() + f2(), you get 4 times the element. So it seems, the calls are done twice, intermediate values, then the result (2*2) is added. Now if you assume that this is done + by +, left to right (operator precedence), the result - which is now a *new* object (+ should create a new list), will be added to the result of the third call (3), so you get 4+3==7. Messy indeed!
20th Jan 2020, 2:40 PM
HonFu
HonFu - avatar
+ 5
Bilbo Baggins I suppose that in both functions, the default param values are retained between calls because the params are list objects which are stored on the heap as reference objects. The default param value will only be initialized once from subsequent function calls. With oddity #1, it's as if it's calling: print([1, 1, 1], [1, 1, 1], [1, 1, 1]) One might have expected: print([1], [1, 1], [1, 1, 1]) However, I'm assuming that the print function doesn't convert the list return values to string until all comma delimited function params are all invoked. Since lists are reference types, the first returned list will point to the same memory reference as the 2nd and 3rd return values. (continued...)
21st Jan 2020, 6:26 AM
David Carroll
David Carroll - avatar
+ 4
I love Python. 😉👌
20th Jan 2020, 10:07 PM
David Carroll
David Carroll - avatar
+ 4
With oddity #2, it's as if calling: print([2] + [2, 2] + [2, 2, 2, 2]) I'm assuming the following inserts the 2nd list into the 1st list, which points to the same object reference: [2] + [2, 2] becomes [2, 2, 2] The 3rd call to f2() will likely then append and return [2, 2, 2] + [2] which is [2, 2, 2, 2]. The 2nd plus operator will then insert [2, 2, 2, 2] into [2, 2, 2], resulting in: [2, 2, 2, 2, 2, 2, 2] I hope this makes sense and helps.
21st Jan 2020, 6:35 AM
David Carroll
David Carroll - avatar
+ 3
Tibor Santa this behaviour is not limited to function arguments, but can be extended to functions dealing with mutable globals (example link above) and generators dealing with mutable globals https://code.sololearn.com/cDT7uRd3vB1e/?ref=app
21st Jan 2020, 6:14 AM
Bilbo Baggins
Bilbo Baggins - avatar
+ 2
I see, sort of undocumented C sequence point... I tried also with (mutable) globals, obtaining exactly the same results. And the result is different if I do (f+f)+f rather than f+(f+f) https://code.sololearn.com/c0GaxUBdUulA/?ref=app
20th Jan 2020, 5:20 PM
Bilbo Baggins
Bilbo Baggins - avatar
+ 2
please don't show this to David Carroll. He loooves python 🙄
20th Jan 2020, 8:16 PM
Selin Genkur
+ 2
David Carroll, this sort of nonsense is not Python-specific. #UndefinedBehavior
20th Jan 2020, 11:01 PM
HonFu
HonFu - avatar
+ 2
This is horrifying. No wonder that mutable function argument is considered an antipattern.
21st Jan 2020, 5:09 AM
Tibor Santa
Tibor Santa - avatar
+ 1
~ swim ~, this is like the precedence intricacies we were talking about a while ago! :)
20th Jan 2020, 5:35 PM
HonFu
HonFu - avatar
+ 1
David Carroll, I don't agree with your analysis of oddity 2 (or I get you wrong). See this version: def f(l=[]): l += [1] return l a = f() # a = [1] b = f() # a AND B = [1, 1] (same ref obj) c = f()+f() print(*map(id, (a, b, c))) c has a different id now. In the moment c is defined, f is called twice, then the reference to that list is kept for now. So 'both' lists are [1, 1]. But then, the lists are added, and for lists and the + operator it is defined that they create a new object. So c will be [1, 1, 1, 1], and it will not change, no matter how often you call f. So oddity 2 is really new object from (old object + old object) + old object, or [1, 1, 1, 1] + [1, 1, 1].
21st Jan 2020, 10:07 AM
HonFu
HonFu - avatar