[Tutor] if...elif question
Danny Yoo
dyoo@hkn.eecs.berkeley.edu
Tue, 2 Oct 2001 19:13:57 -0700 (PDT)
On Wed, 3 Oct 2001, Kalle Svensson wrote:
> [Ignacio Vazquez-Abrams]
> > On Tue, 2 Oct 2001, Chris Keelan wrote:
> > > elif percent >= 60 < 70:
> > It should read "elif percent>=60 and percent<70:". As you have it, it
> > evaluates "percent>=60" and then compares that against 70. Since the result
> > of the first is always less than 70 (either 0 or 1), it is always true.
>
> It does? I thought python special-cased here and evaluated it as
> ((percent >= 60) and (60 < 70))
>
> Same result, anyway.
[WARNING WARNING: If you're starting out with Python, please avoid this
message. I was just curious to see how Python actually understood the
expression "percent >= 60 < 70".]
I've always wanted to play with the Python disassembler, and now's finally
an opportunity to try it out. We can use the disassembler to show exactly
how Python interprets our programs, almost at the "atomic" level:
###
>>> import dis
>>> def mycmp(x):
... return x >= 60 < 70
...
>>> dis.dis(mycmp)
0 SET_LINENO 1
3 SET_LINENO 2
6 LOAD_FAST 0 (x)
9 LOAD_CONST 1 (60)
12 DUP_TOP
13 ROT_THREE
14 COMPARE_OP 5 (>=)
17 JUMP_IF_FALSE 10 (to 30)
20 POP_TOP
21 LOAD_CONST 2 (70)
24 COMPARE_OP 0 (<)
27 JUMP_FORWARD 2 (to 32)
>> 30 ROT_TWO
31 POP_TOP
>> 32 RETURN_VALUE
33 LOAD_CONST 0 (None)
36 RETURN_VALUE
>>> def mycmp2(x):
... return x >= 60 and 60 < 70
...
>>> dis.dis(mycmp2)
0 SET_LINENO 1
3 SET_LINENO 2
6 LOAD_FAST 0 (x)
9 LOAD_CONST 1 (60)
12 COMPARE_OP 5 (>=)
15 JUMP_IF_FALSE 10 (to 28)
18 POP_TOP
19 LOAD_CONST 1 (60)
22 LOAD_CONST 2 (70)
25 COMPARE_OP 0 (<)
>> 28 RETURN_VALUE
29 LOAD_CONST 0 (None)
32 RETURN_VALUE
###
Wow. After staring at:
http://python.org/doc/current/lib/bytecodes.html
for a few minutes, I think I sorta understand what this is doing. Yes,
Python does make a special case when it sees 'a <= b < c', since we can
see that the output of the first is different from the second in many
details.
It feels like the first example is much more complicated compared to the
second, at least in terms the byte-compiled code length. The first, for
example, makes its stack dance around a lot with ROTations here and there.
But I can see why it goes through all the hoops of DUPlicating and
ROTating elements in its internal stack --- it's all there to make sure
that
a <= b < c and z <= b and b < c
behave differently in Python. In the first case, Python will evaluate 'b'
only once. In the second case, 'b' can be evaluated twice. What the
first case is doing by "dup"licating is trying to avoid recomputing 'b'.
For example:
###
>>> def return10():
... print "hello"
... return 10
...
>>> 1 <= return10() <= 100
hello
1
>>> 1 <= return10() and return10() <= 100
hello
hello
1
###
A somewhat silly example, but still sorta neat in a perverse way. *grin*