PEP thought experiment: Unix style exec for function/method calls

Michael ms at cerenity.org
Sun Jul 2 06:49:01 EDT 2006


Carl Banks wrote:

> Maybe look to see how tail-recursive optimization in languages such as
> Scheme work, and whether it can be generalized.

Thanks for the feedback - I should've remembered tail recursion.

> I doubt this would be possible without a major change in how functions
> work in Python.  The most obvious problem is that functions refer to
> local variables by an index, not by name.  If you were to execute the
> bytecode of one function in the stack frame of another, Bad Things
> would happen.

Oh that's a pity. I can see why you'd do that, but it is a pity. That would
tend to imply that _replacing_ rather than _reusing_ the top frame is the
most doable/likely/sensible approach. (It's also very obviously easier to
emulate)

(And yes, I was consider reusing or replacing *only* the top stack frame.
Replacing seems better with retrospect, even if reusing seemed like a fun
idea :-)

> >     def set_name():
> >         name = raw_input("Enter your name! > ")
> >         cexe greet()
> >
> >     def greet():
> >         print "hello", name
> >
> >     cexe set_name()
> >     print "We don't reach here"
> >     ----------
> >
> > This would execute, ask for the user's name, say hello to them and then
> > exit - not reaching the final print "We don't reach here" statement.
> 
> Only if you were to replace the whole stack.  If you only replace or
> reuse the top frame, I would think greet would exit and execution would
> resume right after the point from which set_name was called.  Or am I
> misunderstanding what you want?

I think you are. 

In the hypothetical example, your code by definition gets called by
something. This leave a hypothetical return point. For the moment, lets
make things clearer by what I mean by changing the example to this:

> > 1     def set_name():
> > 2         name = raw_input("Enter your name! > ")
> > 3         cexe greet()
> > 4
> > 5     def greet():
> > 6         print "hello", name
> > 7
> > 8     def main():
> > 9         cexe set_name()
> > 10        print "We don't reach here"
> > 11
> > 12    main()
> > 13    print "see what I mean?"
> >     ----------

at line 12, we call 8. We can argue then our stack looks like this:
[ { "context" : "main", pc : 8 }, { "context" : "__main__", pc : 12 }, ]

(I'm going to push/pop at the front of the list)

We reach line 9, before we execute the code there:
[ { "context" : "main", pc : 9 }, { "context" : "__main__", pc : 12 }, ]

After we execute, it does a cexe call of set_name, not a normal call of
set_name. This causes the top stack frame to be _replaced_ :

[ { "context" : "set_name", pc : 1 }, { "context" : "__main__", pc : 12 }, ]

We then carry on, until we reach line 3, at which point before execution our
stack would look something like:

[ { "context" : "set_name", pc : 3 }, { "context" : "__main__", pc : 12 }, ]

And after:

[ { "context" : "greet", pc : 5 }, { "context" : "__main__", pc : 12 }, ]

We'd then execute line 6, and after executing that line, our stack would
look like this:

[ { "context" : "greet", pc : 6 }, { "context" : "__main__", pc : 12 }, ]

We're falling off the end of a function at that point, so we'd pop the top
stack frame, as follows:
[ { "context" : "__main__", pc : 12 }, ]

Which means we return to the line after 12, and continue on with line 13
print "see what I mean". That means the '''print "We don't reach here"'''
code isn't executed.

>> * Am I mad? :)
> 
> Yep. :)

Thought so!

Thanks :-)


Michael.




More information about the Python-list mailing list