Uplevel functionality

M

Maciej Sobczak

Hello,

I would like to know if there is any possibility in Python to execute
arbitrary scripts in the context higher in the stack frame.

In essence, I would like to have the equivalent of the following Tcl code:

proc repeat {n script} {
for {set i 0} {$i < $n} {incr i} {
uplevel $script
}
}

This allows me to do:

repeat 5 {puts "hello"}

prints:
hello
hello
hello
hello
hello

Or this:

set i 10
repeat 5 {incr i}
puts $i

prints:
15

That second example shows that the script provided as a second parameter
to the "repeat" procedure (the script is "incr i") is executed in the
context where the procedure was called, not locally in the procedure itself.

The strongest analogy to the above repeat procedure in Tcl would be a
hypothetical Python function:

def repeat(n, script):
for i in xrange(n):
EVALUATE script HIGHER IN THE STACK #???


Thank you very much,
 
R

Robin Becker

How about this, apparently f_locals/globals are readonly so you won't be
allowed arbitrary scopes.

############################
C:\tmp>cat repeat.py
def repeat(n, script):
from sys import _getframe
f = _getframe(1)
L = f.f_locals
G = f.f_globals
for i in xrange(n):
exec script in G, L

def test():
i = 0
repeat(3,'print i;i+=5')

if __name__=='__main__':
test()

C:\tmp>repeat.py
0
5
10

C:\tmp>
############################
 
R

Robin Becker

However, by using an exec '' the following can be made to work. The exec
is used to disable the unwanted optimisation of print k into a name
error.

##################################
def repeat(n, script):
from sys import _getframe
f = _getframe(1)
L = f.f_locals
G = f.f_globals
for i in xrange(n):
exec script in G, L

def test():
i = 0
repeat(3,'print i;i+=5')
exec ''
repeat(3,'k=2')
print k

if __name__=='__main__':
test()
##################################
 
L

Levente Sandor

Maciej Sobczak said:
Hello,

I would like to know if there is any possibility in Python to execute
arbitrary scripts in the context higher in the stack frame.

In essence, I would like to have the equivalent of the following Tcl code:

proc repeat {n script} {
for {set i 0} {$i < $n} {incr i} {
uplevel $script
}
}

This allows me to do:

repeat 5 {puts "hello"}

prints:
hello
hello
hello
hello
hello

Or this:

set i 10
repeat 5 {incr i}
puts $i

prints:
15

That second example shows that the script provided as a second parameter
to the "repeat" procedure (the script is "incr i") is executed in the
context where the procedure was called, not locally in the procedure itself.

The strongest analogy to the above repeat procedure in Tcl would be a
hypothetical Python function:

def repeat(n, script):
for i in xrange(n):
EVALUATE script HIGHER IN THE STACK #???

Yes, it would be

def repeat(n, script):
for i in xrange(n):
exec(script, locals(), globals())

repeat(5, 'print "hello"')

i = 10
repeat(5, 'i += 1')
print i

Thank you very much,

With pleasure,
 
T

Terry Reedy

Maciej Sobczak said:
Hello,

I would like to know if there is any possibility in Python to execute
arbitrary scripts in the context higher in the stack frame.

An alternate answer from those already posted is to not create the new
context that you want to uplevel from. Seriously.
In essence, I would like to have the equivalent of the following Tcl code:

proc repeat {n script} {
for {set i 0} {$i < $n} {incr i} {
uplevel $script
}
}

As I read this, this defines the repeat function as an abbreviation of a
'for' loop. The uplevel is only needed because the abbreviating process
creates a nested frame. In Lisp, repeat would be written a text macro
which does the substitution in the same frame, and hence the frame problem
and need for uplevel would not arise. In Python, we currently live without
the pluses and minuses of macros and usually write out the for loops ;-)
This allows me to do:

repeat 5 {puts "hello"}

prints:
hello
hello
hello
hello
hello

for i in range(5): print 'hello' # does same thing.
repeat(5, "print 'hello'") # even if possible, saves all of 5 key
strokes
repeat(5, lambda: print 'hello') # possible, without uplevel, takes 2 more
Or this:

set i 10
repeat 5 {incr i}
puts $i

prints:
15

i=10
for n in range(5): i+=1
print i

In batch mode, which requires 'print ', 36 instead of 33 keystrokes.

You *can* do this in current Python:

def repeat(num, func):
for i in range(num): func()

def inci(): i[0]+=1 # need def since lambda only abbreviates 'return
expression'

i=[10]
repeat(5, inci)
15

Python goes for clarity rather than minimal keystrokes. Example:
It has been proposed that 'for i in n:' be equivalent to 'for i in
range(n):'.
Given the set theory definition n == {0, ..., n-1} (==set(range(n))),
this is theoretically sensible, but was judged too esoteric for most
people.

Terry J. Reedy
 
C

Christos TZOTZIOY Georgiou

for i in range(5): print 'hello' # does same thing.
repeat(5, "print 'hello'") # even if possible, saves all of 5 key
strokes
repeat(5, lambda: print 'hello') # possible, without uplevel, takes 2 more

print is invalid in a lambda (like other statements) as you most surely
know. I would guess the champagne is to blame? :) Happy new year!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,174
Messages
2,570,940
Members
47,485
Latest member
Andrewayne909

Latest Threads

Top