1

I was testing with something and I wrote this code for example purposes.I felt the output I got was weird.I expected that function calls are executed one after the other but according to this code both the print statements are getting executed after each other and then the return values are printed together.What does this mean and what is the flow of the code in this case?

global num
num=5

def demo(num):
    num+=1
    print("hi")
    return(num)



print(demo(num),demo(num))

output-

hi
hi
6 6
ubuntu_noob
  • 2,305
  • 6
  • 23
  • 64
  • 1
    @Patrick Haugh This is not a duplicate of the question you marked. In this question, it's simply because the last `print` function needs to have all of its arguments evaluated before it can output anything, so both calls to `demo()` have to finish first and therefore both `hi`s are printed before the numbers. – blhsing Jul 20 '18 at 23:54
  • @blhsing do the function calls evaluate from left to right? – ubuntu_noob Jul 20 '18 at 23:56
  • 1
    The `num` in your function is not the `global num`. It is the local parameter with the same name. You are modifying the local copy of the global `num`. To see what you want to see, remove the formal parameter from the function definition whatsoever and move `global num` into the function definition. – DYZ Jul 21 '18 at 00:03
  • What output were you expecting? I _think_ I've guessed where you confusion is, and written an answer to explain it, but I could be wrong. – abarnert Jul 21 '18 at 00:27

2 Answers2

3

Program evaluation has an order of operations just like arithmetic does. And similarly, it's not always intuitive, especially when we "consume" things left to right, up to down while reading.

So, what gives? Lets become the python interpreter and see why order of operations is important.

# A wild statement appears. Time to compute!
print(demo(num),demo(num))
# I cant't print yet! i'm missing info! 

I need to evaluate this first demo(num), and by default im going to do it a closed room away from globals

# evaluating demo(num=5) - num here is a new local variable, it has no relation to the global one defined above
num+=1 # num = 6
print("hi") # [[[PRINT hi]]] ~> to console
return 6 # returns the value 6 filling in another piece of the puzzle

Where are we at now? Almost ready to call this print, just need to do this demo thing again

print(6, demo(num))
# What is num though? 
# Well, the only num I know of out here in global space is 5
print(6, demo(5))
# evaluate: demo(5)

This seems familiar!

# evaluating: demo(num=5) - again, a NEW local variable is created just for this function call
num+=1 # num = 6
print("hi") # [[[PRINT hi]]] ~> to console
return 6

Finally, print has all its arguments it needs

print(6, 6) # [[[PRINT 6 6]]] ~> to console

print is not magic, it's just another function! Same as the demo you wrote.

And functions will not evaluate until all their parameters are supplied.

print(a, b) needs the values of a & b before it can do its thing.

Ken Colton
  • 1,046
  • 10
  • 15
  • 1
    Wish I had more time to edit down and make a nice tree diagram showing this with much less words, but gotta sign off for now! Hope this "ELI5" version is helpful for someone. – Ken Colton Jul 21 '18 at 01:57
2

I expected that function calls are executed one after the other

That's exactly what happens.

But there's no way the print can happen before the demo calls, because it's trying to print out the values returned by those calls. (You can loosely think of this as a special case of anywhere you see parentheses: 2 * (3+4) can't multiply by 2 until it's added 3+4, and print(demo(num), demo(num)) can't print the results of demo(num) and demo(num) until it's called them. But don't take that too literally.)

So, those demo calls happen left to right, then the print call happens.


In more detail, let's step through how it evaluates this line:

print(demo(num),demo(num))

… Python has to do this:

  • Evaluate print by looking it up as a builtin name, which finds the builtin print function.
  • Evaluate the first argument.
    • Evaluate demo by looking it up as a global name, which finds the global demo function that you defined.
    • Evaluate num by looking it up as a global name, which finds the global 5 value.
    • Call the function on the argument.
    • The parameter num gets the value passed in, 5.
    • num += 1 updates the local variable (parameters are local variables) num to 6.
    • print("hi") prints out hi.
    • return(num) returns the value of the local variable, 6.
  • Evaluate the second argument.
    • … same as above, it prints out hi and returns 6.
  • Call the function returned by evaluating print on the two arguments returned by the two calls, so it prints out 6 6.

If you want the rigorous definition, he details are covered in Calls in the reference documentation. In particular (stripping out irrelevant bits)

call ::= primary "(" [argument_list] ")"

 …

The primary must evaluate to a callable object…. All argument expressions are evaluated before the call is attempted.


according to this code both the print statements are getting executed after each other and then the return values are printed together

Yes. The two function calls have to be executed in order, so that it can get the values to pass to the print function. Executing them prints out Hi twice. Then it has all the values, so it can print them, which prints out 6 6, since both values are 6.

abarnert
  • 354,177
  • 51
  • 601
  • 671