0

I would like to understand the way Python evaluates function calls inside print statements or, more particulary, what does the call for next(some_iter) return? A mutable reference to the element in the next position or just a copy of the element in the next position when some_iter was last evaluated?

The following code:

def foo(val, arr=set()):
    arr.add(val)
    return arr

print(foo(1))
print(foo(2))
print(foo(1), foo(3), foo(0))

Returns the output:

{1}
{1, 2}
{0, 1, 2, 3} {0, 1, 2, 3} {0, 1, 2, 3}

Python calls the function three times, holding what seems to be a reference to arr, then, since sets are mutable, the output is as can be seen above. foo(1) return value was not saved, that is {1, 2} when it is called in order of the print statement.

However

lst = [1, 2, 3, 4]
def bar(a):
    a = lst
    a[0] = 17
    return a
x = iter(lst)
y = iter(lst)
print(next(x), bar(lst), next(y), next(x))
print(lst)

Would print

1 [17, 2, 3, 4] 17 3
[17, 2, 3, 4]

I would have expected

17 [17, 2, 3, 4] 17 3
[17, 2, 3, 4] 

That is for next to hold a reference to the first element of lst, which would later be changed to 17, and for that reference to be printed as 17 eventually, but it is computed first returning 1.

Why does it not happen? In what way is the next() or iters different?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • I fail to see how this related to my question, sure muteable default values behave as they do, although the part that troubles me is the order of operations and evaluations in regards to the next() function. Thanks a lot by the way, –  Mar 26 '17 at 21:04
  • 1
    Any function call always evaluates its arguments before calling the function. The first `next(x)` returns 1 because that's what's there at the time. `bar(lst)` happens after that 1 has been determined. `print` has no control over the process. – Alex Hall Mar 26 '17 at 21:07
  • @jonrsharpe I made the same mistake of voting to close the moment I saw a mutable default argument, but it seems OP understands that and is not asking about it. Not that I understand how the first part of the question relates to the second. – Alex Hall Mar 26 '17 at 21:08
  • @jonrsharpe - I think this one is more related to the way a function call's arguments are evaluated. `foo` uses a mutable default argument shared among all calls, but `bar` references a global list, which has the same effect. – TigerhawkT3 Mar 26 '17 at 21:08
  • So foo(1) returns a reference to the set(it wouldn't have printed {0,1,2,3} otherwise) but next(x) is evaluated fully? –  Mar 26 '17 at 21:09
  • `foo` mutates the set `arr`, `bar` does not mutate 1, it replaces it by mutating the list. – Alex Hall Mar 26 '17 at 21:10
  • I see, thanks a lot! I would go try some things then. –  Mar 26 '17 at 21:12
  • 1
    The key is that the first `next(x)` is evaluated _before_ `lst` is modified by `bar`. – TigerhawkT3 Mar 26 '17 at 21:14
  • With `foo`, it adds 1 and shows you the result, adds 2 and shows you the result, then adds 1, 3, and 0, and _then_ shows you the result three times. – TigerhawkT3 Mar 26 '17 at 21:18
  • Yes, I think my problem was that even though lst is mutable, lst[0] is not and thefore changing it later does not affect the previous next call –  Mar 26 '17 at 21:21
  • No, that's not it. You're retrieving the first value of `x` (which is an iterator around `lst`), saving that, and only later changing the first value of `lst`. After you change that first value to something new with the `bar` call, `next(y)` will grab that new first value. – TigerhawkT3 Mar 26 '17 at 21:24

0 Answers0