Persistent variables in python

B

buffi

Found out a quite fun way to store persistent variables in functions in
python.

def doStuff():
try:
#Will throw exception if not set
doStuff.timesUsed

##
#Insert stuff to do each time the function is called here
##
doStuff.timesUsed+=1
print "Function call!"

except AttributeError:
doStuff.timesUsed = 0 # set up the variable

##
#Put other stuff to call the first
#time the function is called here
##
print "First call!"

doStuff() #recursive call. Will not throw exception now

------------

Some output testing this:
First call!
Function call!4

------------


Is this concidered bad coding practice since I guess persistent
variables in functions are not meant to be?

/buffi (www.buffis.com)
 
G

Gabriel Genellina

def doStuff():
try:
#Will throw exception if not set
doStuff.timesUsed

Is this concidered bad coding practice since I guess persistent
variables in functions are not meant to be?

I don't think so, since Python proudly says that functions are
first-class objects.
CherryPy does a similar thing to mark a method as "exposed".

But perhaps I'd write the code this way to avoid an unneeded and
risky recursive call:

def doStuff(some, arguments, may, *be, **required):
try:
doStuff.timesUsed += 1
except AttributeError:
doStuff.timesUsed = 1
# ... special case for first call ...
# ...common code...

If you need to code something special for the 2nd and following
calls, add an else: clause


--
Gabriel Genellina
Softlab SRL






__________________________________________________
Preguntá. Respondé. Descubrí.
Todo lo que querías saber, y lo que ni imaginabas,
está en Yahoo! Respuestas (Beta).
¡Probalo ya!
http://www.yahoo.com.ar/respuestas
 
B

buffi

I don't think so, since Python proudly says that functions are
first-class objects.
CherryPy does a similar thing to mark a method as "exposed".

But perhaps I'd write the code this way to avoid an unneeded and
risky recursive call:

def doStuff(some, arguments, may, *be, **required):
try:
doStuff.timesUsed += 1
except AttributeError:
doStuff.timesUsed = 1
# ... special case for first call ...
# ...common code...

True, the recursivity is not needed there I guess :)

It just feels so ugly to use try/except to enable the variable but I've
found it useful at least once.

/buffi (buffis.com)
 
L

Lee Harr

Found out a quite fun way to store persistent variables in functions in
python.
Is this concidered bad coding practice since I guess persistent
variables in functions are not meant to be?


I am using is in one of my recent projects. I was thinking of
it sort of like "static" variables in C.

I use a decorator to create the variables ...


def static(**kw):
'''
Used to create a decorator function that will add an
attribute to a function and initialize it.
... def bar():
... print bar.foo
... bar.foo += 1
... 6
'''

def decorator(f):
f.__dict__.update(kw)
return f
return decorator
 
S

Steven D'Aprano

True, the recursivity is not needed there I guess :)

It just feels so ugly to use try/except to enable the variable but I've
found it useful at least once.

That's a matter of taste. Try replacing the try...except block with
hasattr:

def doStuff():
if hasattr(doStuff, timesUsed):
doStuff.timesUsed += 1
else:
doStuff.timesUsed = 1
do_common_code

Here is another alternative, using the fact that Python creates default
values for arguments once when the function is compiled, not each time it
is run:

def doStuff(some, *arguments,
# don't mess with the following private argument
__private={'timesUsed': 0, 'otherData': 'Norwegian Blue'}):
""" Do stuff with some arguments. Don't pass the __private
argument to the function unless you know what you are doing,
it is for private use only.
"""
__private['timesUsed'] += 1
do_common_code
 
D

Duncan Booth

buffi said:
Is this concidered bad coding practice since I guess persistent
variables in functions are not meant to be?
There is a problem that this trick only works for functions and not for
methods as it assumes that there is a global name through which you can
access the function.

I think that more significantly is the question how you initialize the
value (Lee Harr's decorator does that quite nicely though) or reset it, or
have multiple instances each with their own saved value. As soon as you
start asking those types of questions it is time to switch to using a
class.

So in short, yes it is a plausible trick in some situations as a
lightweight alternative to a class which has only static state and one
method, but if you want a second method or multiple instances you'll want a
real class. (And yes, I do realise that by embedding the function with
state inside a factory function you can create multiple instances.)

Python also provides another lightweight way of saving state between calls.
Consider whether your particular use can be written as a generator:

def dostuff():
timesused = 0
print "First call!"
while 1:
timesused += 1
print "Function call", timesused
yield timesused
First call!
Function call 1
1Function call 2
2Function call 3
34
 
B

buffi

There is a problem that this trick only works for functions and not for
methods as it assumes that there is a global name through which you can
access the function.

I didn't really see any issue with this since methods can store the
persistant data from the method inside the class containing it :)

/buffi
 
E

eduardo.padoan

That's a matter of taste. Try replacing the try...except block with
hasattr:

def doStuff():
if hasattr(doStuff, timesUsed):
doStuff.timesUsed += 1
else:
doStuff.timesUsed = 1
do_common_code

Ok, it is a matter of taste and I prefer the try/except way, but with
hasattr you will do a function call and a intern 'truthness'
verification each time you increment .timeUsed. With try/except, after
the first time, you do nothing else than increment it.
 
G

Gabriel Genellina

At said:
That's a matter of taste. Try replacing the try...except block with
hasattr:

def doStuff():
if hasattr(doStuff, timesUsed):
doStuff.timesUsed += 1
else:
doStuff.timesUsed = 1
do_common_code

Using hasattr you end checking every access to "timesUsed" twice.
Using a try/except, after the initial setup, there is no additional overhead.
hasattr does exactly that: it calls getattr to see if it raises an
exception (this is documented behavior, not a hidden implementation detail).
In Python it's far more common EAFP ('easier to ask for forgiveness
than permission') than LBYL ('look before you leap').


--
Gabriel Genellina
Softlab SRL






__________________________________________________
Preguntá. Respondé. Descubrí.
Todo lo que querías saber, y lo que ni imaginabas,
está en Yahoo! Respuestas (Beta).
¡Probalo ya!
http://www.yahoo.com.ar/respuestas
 
S

Steven D'Aprano

Ok, it is a matter of taste and I prefer the try/except way, but with
hasattr you will do a function call and a intern 'truthness'
verification each time you increment .timeUsed. With try/except, after
the first time, you do nothing else than increment it.

That's not quite true. You do have to set up the try...except machinery,
but that's very cheap.

In any case, I didn't say that I like the hasattr idiom, merely that it is
available.
 

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,981
Messages
2,570,187
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top