This is an interaction between two different things:
1) Function argument defaults in Python are not recalculated at each invocation, but at function definition time (bigger discussion at "Least Astonishment" and the Mutable Default Argument)
2) Python needs to evaluate all arguments to a function before it will call the function (e.g. to perform print(1+2, 5*3), 1+2 and 5*3 will need to be calculated before print is even considered)
Thus, if you call f multiple times without a second argument, it will just append to the same array you declared originally in the def. This is what you can clearly see in the second example. The first invocation prints arr after the first mutation ([1]); the second prints arr after the second one ([1, 2]).
The first example is different because of the second principle I stated: all the parameters are evaluated before the function is called. So print(f(1), f(2)) will first invoke f(1), changing arr to [1]; then f(2) will be evaluated, changing arr to [1, 2]. Since both function calls returned the reference to arr, print will then print the content of arr twice: [1, 2], [1, 2].