[Python-Dev] [PEP] += on return of function call result (original) (raw)

Luke Kenneth Casson Leighton lkcl@samba-tng.org
Mon, 19 May 2003 09:08:11 +0000


hiya jeff,

on radio 4 today there was a discussion about art - what makes people go "wow" instead of being shocked. seeing the byte code in front of my eyes isn't so much of a shock, more of a "wow" because i have at some point in my past actually looked at the python sources stack machine, for investigating parallelising it (!!!!!)

okay.

how do i run the examples you list? dis.dis(f) gives an "unrecognised variablename dis".

okay. let's give this a shot.

Script started on Mon May 19 08:44:19 2003 lkcl@highfield:~$ python O Python 2.2.2 (#1, Jan 18 2003, 10🔞59) [GCC 3.2.2 20030109 (Debian prerelease)] on linux2 Type "help", "copyright", "credits" or "license" for more information.

import dis def g(): return f(x) ... dis.dis(g) 0 LOAD_GLOBAL 0 (f) 3 LOAD_GLOBAL 1 (x) 6 CALL_FUNCTION 1 9 RETURN_VALUE
10 LOAD_CONST 0 (None) 13 RETURN_VALUE
def g(): f(x) ... dis.dis(g) 0 LOAD_GLOBAL 0 (f) 3 LOAD_GLOBAL 1 (x) 6 CALL_FUNCTION 1 9 POP_TOP
10 LOAD_CONST 0 (None) 13 RETURN_VALUE
lkcl@highfield:~$ exit

Script done on Mon May 19 08:44:56 2003

right.

the difference between these two is the POP_TOP.

so, the return result is placed on the stack, from the call to f(x).

so... if there's instead an f(x) += 1 instead of f(x), then the result is going to be pushed onto the top of the stack, followed by the += 1, followed at the end by a POP_TOP.

if the result is used (e.g. assigned to a variable),

x = f(x) += 1

then you don't do the POP_TOP.

... am i missing something?

what am i missing?

that it's not known what type of variable is returned, therefore you're not certain as to what type of STORE to use?

Script started on Mon May 19 08:51:22 2003 lkcl@highfield:~$ python -O Python 2.2.2 (#1, Jan 18 2003, 10🔞59) [GCC 3.2.2 20030109 (Debian prerelease)] on linux2 Type "help", "copyright", "credits" or "license" for more information.

def f(): return 5 ... def g(): ... x = f() + 1 ... return x ... import dis dis.dis(g) 0 LOAD_GLOBAL 0 (f) 3 CALL_FUNCTION 0 6 LOAD_CONST 1 (1) 9 BINARY_ADD
10 STORE_FAST 0 (x) 13 LOAD_FAST 0 (x) 16 RETURN_VALUE
17 LOAD_CONST 0 (None) 20 RETURN_VALUE
lkcl@highfield:~$ Script done on Mon May 19 08:52:40 2003

okay... soo.... you get an assignment into a variable... ...

okay, i think i see what the problem is.

because the return result may not be used, you don't know what type of STORE to use?

or, because there are optimisations added, it's not always possible to "pass down" the right kind of STORE_xxx to the previous stack level?

i believe you may be thinking that this is more complex than it is. that's very patronising of me. scratch that.

i believe this should not be complex :)

"+=" itself is a function call with two arguments and a return result, where the return result is the first argument.

it just happens that that function call has been drastically optimised - with its RETURN_VALUE removed; STORE_xxx removed.

more thought needed. i'll go look at some code.

l.

p.s. 10 and 13 in the 8:52:40am typescript above look like they could be optimised / removed.

p.p.s. yes i have written a stack-machine optimiser before.

On Sat, May 17, 2003 at 10:21:39AM -0500, Jeff Epler wrote:

I think that looking at the generated bytecode is useful.

# Running with 'python -O' >>> def f(x): x += 1 >>> dis.dis(f) 0 LOADFAST 0 (x) 3 LOADCONST 1 (1) 6 INPLACEADD 7 STOREFAST 0 (x) *** 10 LOADCONST 0 (None) 13 RETURNVALUE >>> def g(x): x[0] += 1 >>> dis.dis(g) 0 LOADGLOBAL 0 (x) 3 LOADCONST 1 (0) 6 DUPTOPX 2 9 BINARYSUBSCR 10 LOADCONST 2 (1) 13 INPLACEADD 14 ROTTHREE 15 STORESUBSCR *** 16 LOADCONST 0 (None) 19 RETURNVALUE >>> def h(x): x.a += 1 >>> dis.dis(h) 0 LOADGLOBAL 0 (x) 3 DUPTOP 4 LOADATTR 1 (a) 7 LOADCONST 1 (1) 10 INPLACEADD 11 ROTTWO 12 STOREATTR 1 (a) *** 15 LOADCONST 0 (None) 18 RETURNVALUE In each case, there's a STORE step to the inplace statement. In the case of the proposed def j(x): x() += 1 what STORE instruction would you use? >>> [opname for opname in dis.opname if opname.startswith("STORE")] ['STORESLICE+0', 'STORESLICE+1', 'STORESLICE+2', 'STORESLICE+3', 'STORESUBSCR', 'STORENAME', 'STOREATTR', 'STOREGLOBAL', 'STOREFAST', 'STOREDEREF'] If you don't want one from the list, then you're looking at substantial changes to Python.. (and STOREDEREF probably doesn't do anything that's relevant to this situation, though the name sure sounds promising, doesn't it) Jeff

--

expecting email to be received and understood is a bit like picking up the telephone and immediately dialing without checking for a dial-tone; speaking immediately without listening for either an answer or ring-tone; hanging up immediately and then expecting someone to call you (and to be able to call you).

every day, people send out email expecting it to be received without being tampered with, read by other people, delayed or simply - without prejudice but lots of incompetence - destroyed.

please therefore treat email more like you would a CB radio to communicate across the world (via relaying stations): ask and expect people to confirm receipt; send nothing that you don't mind everyone in the world knowing about...