Function to execute only once

P

PyPK

Hi if I have a function called
tmp=0
def execute():
tmp = tmp+1
return tmp

also I have
def func1():
execute()
....
and
def func2():
execute()
....

now I want execute() function to get executed only once. That is the
first time it is accessed.
so taht when funcc2 access the execute fn it should have same values as
when it is called from func1.
 
J

Jaime Wyant

If I understand you correctly, you want `tmp' to be global...

If so, declare it as so in execute ->

def execute():
global tmp
tmp = tmp+1
return tmp

Otherwise, what happens is that you declare a variable local to
execute, that is named tmp. When the assignment occurs it uses the
global value of `tmp', which is 0, and adds it to the *local* tmp.

I hope that is not too confusing.

jw
 
P

Paul Rubin

PyPK said:
now I want execute() function to get executed only once. That is the
first time it is accessed.
so taht when funcc2 access the execute fn it should have same values as
when it is called from func1.

There's nothing built into Python for that. You have to program the
function to remember whether it's already been called. It sounds like
you're trying to avoid performing some side effect more than once.

Anyway, there's multiple ways you can do it. The conceptually
simplest is probably just use an attribute on the function:

tmp = 0 # you want to modify this as a side effect
def execute():
global tmp
if not execute.already_called:
tmp += 1
execute.already_called = True
return tmp
execute.already_called = False

Other ways include using a class instance, using an iterator, etc.

Generally too, you might find it cleaner to avoid having side effects
like that. Instead, put tmp itself inside a class instance:

class Memo:
def __init__(self):
self.value = 0 # any side effects operate on this
self.already_called = False

def __call__(self):
if not self.already_called:
self.already_called = True
self.value += 1
return self.value

execute = Memo()

Now you don't have global state cluttering things up, you can make
multiple instances easily, etc.
 
B

Benji York

PyPK said:
now I want execute() function to get executed only once. That is the
first time it is accessed.

How about just calculating the value at import time?
 
S

snoe

I've been seeing alot about decorators and closures lately and my
initial thought was that this would be a good place to use them instead
of wrapping it around a class. That was my initial thought :) What I
came up with was this:
def execute_once(fn):
result = None
def executor(*args, **kwargs):
if not result:
result = fn(*args, **kwargs)
return result
return executor

@execute_once
def execute(tmp):
tmp = tmp+1
return tmp

def func1(tmp):
execute(tmp)

def func2(tmp):
execute(tmp)

tmp=0
print 'init tmp:', tmp
func1(tmp)
print 'ran func1 tmp:', tmp
func2(tmp)
print 'ran func2 tmp:', tmp

It gives the following error:
init tmp: 0
Traceback (most recent call last):
File "C:\Download\test.py", line 26, in ?
func1(tmp)
File "C:\Download\test.py", line 19, in func1
execute(tmp)
File "C:\Download\test.py", line 5, in executor
if not result:
UnboundLocalError: local variable 'result' referenced before assignment

Makes sense to me, except I expected some closure 'magic'. I thought
that when I wrapped executor() inside execute_once() the name result
would be available to executor(). What am I missing here (I expect the
answer to be 'alot') but is this type of solution valid for this
problem?
 
S

snoe

The problem seemed to be because I was rebinding result inside
executor. Can someone explain why it works below but not in the first
one?

Also why is it if I set tmp as a global and don't pass it as a
paremeter to the various functions as per the OP that I get an
"UnboundLocalError: local variable 'tmp' referenced before assignment"?

def execute_once(fn):
print "in execute_once"
result = {}
def executor(*args, **kwargs):
if fn not in result:
result[fn] = fn(*args, **kwargs)
return result[fn]
return executor

@execute_once
def execute(tmp):
print "in execute"
tmp = tmp+1
return tmp

def func1(tmp):
return execute(tmp)

def func2(tmp):
return execute(tmp)

tmp = 0
print 'init tmp:', tmp
tmp = func1(tmp)
print 'ran func1 tmp:', tmp
tmp = func2(tmp)
print 'ran func2 tmp:', tmp
tmp = func1(tmp)
print 'ran func1 tmp:', tmp

OUTPUT:
in execute_once
init tmp: 0
in execute
ran func1 tmp: 1
ran func2 tmp: 1
ran func1 tmp: 1
 
P

Paul Rubin

snoe said:
Also why is it if I set tmp as a global and don't pass it as a
paremeter to the various functions as per the OP that I get an
"UnboundLocalError: local variable 'tmp' referenced before assignment"?

If you don't declare it as a global, and if you try to assign a value
to it, then Python thinks it's a local. If you only refer to it and
never assign it, Python decides it's global. Python is weird that
way. One consequence is that if it's local to some outer scope, then
it's neither global nor local to your function, so there's no way to
assign to it.

I think Pythonic style is to not do complex things with closures,
but to use class instances instead.
 
B

Bengt Richter

Hi if I have a function called
tmp=0
def execute():
tmp = tmp+1
return tmp

also I have
def func1():
execute()
....
and
def func2():
execute()
....

now I want execute() function to get executed only once. That is the
first time it is accessed.
so taht when funcc2 access the execute fn it should have same values as
when it is called from func1.

You could have the execute function replace itself with a function
that returns the first result from there on, e.g., (assuming you want
the global tmp incremented once (which has bad code smell, but can be expedient ;-)):
... global tmp, execute
... tmp = cellvar = tmp + 1
... def execute():
... return cellvar
... return tmp
... ... return execute() # so we can see it
... ... return execute() # so we can see it
... 5 0 LOAD_DEREF 0 (cellvar)
3 RETURN_VALUE

But if you want to call the _same_ "execute" callable that remembers that
it's been called and does what you want, you need a callable that can
remember state one way or another. A callable could be a function with
a mutable closure variable or possibly a function attribute as shown in
other posts in the thread, or maybe a class bound method or class method,
or even an abused metaclass or decorator, but I don't really understand
what you're trying to do, so no approach is likely to hit the mark very well
unless you show more of your cards ;-)

Regards,
Bengt Richter
 
?

=?ISO-8859-1?Q?Lasse_V=E5gs=E6ther_Karlsen?=

Bengt Richter wrote:
... global tmp, execute
... tmp = cellvar = tmp + 1
... def execute():
... return cellvar
... return tmp
<snip>

On man did this put my head into a spin :p
 

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

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,135
Latest member
VeronaShap
Top