Joost
Joost

Reputation: 546

Unexpected Python print behavior

When I try this code in Python 2.7.3:

names = ["Paul", "Mary", "Susan"]
names.sort()

def valuate(string):
    print ord('A')
    return sum(ord(s) for s in string)

i = 1
for name in names:
    print i, name, valuate(name)
    i += 1

I expect the output:

65
1 Mary 409
65
2 Paul 402
65
3 Susan 522

But instead I get:

1 Mary 65
409
2 Paul 65
402
3 Susan 65
522

It seems the print statement outputs the i and name values before calling the function. Is that so? Why?

Upvotes: 2

Views: 346

Answers (5)

Blckknght
Blckknght

Reputation: 104682

The surprise you're encountering is that the print statement prints out each of the expressions it is given before evaluating the next one. That is, a statement like print A, B, C is equivalent to:

print A, # note, the trailing comma suppresses the newline
print B,
print C

As you'd expect from separate statements, A gets written out before B or C is evaluated.

That surprise is perhaps part of the reason that Python 3 has done away with the print statement in favor of a builtin print function which behaves more like you expect (all of its arguments are evaluated before the function runs).

In Python 2, you can get the Python 3 style print if you want using a future import:

from __future__ import print_function

Upvotes: 2

NPE
NPE

Reputation: 500157

The sequence of events is as follows:

print i,
print name,
val = valuate(name) # prints ord('A')
print val

This is confirmed by looking at the bytecodes generated for print i, name, valuate(name):

 11          19 LOAD_FAST                0 (i)
             22 PRINT_ITEM          
             23 LOAD_FAST                1 (name)
             26 PRINT_ITEM          
             27 LOAD_GLOBAL              1 (valuate)
             30 LOAD_FAST                1 (name)
             33 CALL_FUNCTION            1
             36 PRINT_ITEM          
             37 PRINT_NEWLINE       

I don't know if this evaluation order is guaranteed (a quick search didn't reveal anything). I definitely wouldn't rely on it, and therefore wouldn't write code like this.

Upvotes: 4

zenpoy
zenpoy

Reputation: 20126

I think that a small ',' would make things a lot more simple to grasp.

names = ["Paul", "Mary", "Susan"]
names.sort()

def valuate(string):
    print ord('A'),
    return sum(ord(s) for s in string)

i = 1
for name in names:
    print i, name, valuate(name)
    i += 1

Now that outputs:

1 Mary 65 409
2 Paul 65 402
3 Susan 65 522

The comma after print makes it not to print a new line character at the end. So now you can see that valuate first prints the ord('A') and then returns the output for the outer print. (I also indented the i += 1 line)

Upvotes: 0

Diego Basch
Diego Basch

Reputation: 13059

It's very simple. Your print statement evaluates the arguments lazily. It prints i first, then name, and then it calls valuate. Valuate prints 65. Then your print statement prints the result of valuate.

Upvotes: 3

jimhark
jimhark

Reputation: 5046

It's not just the print statement, it's the function call.

print i, name, valuate(name)

Prints i, then name, then calls valuate which prints 65 (and a new line) then on return the print statement continues to print the return value (and another newline).

Upvotes: 6

Related Questions