By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,907 Members | 1,832 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,907 IT Pros & Developers. It's quick & easy.

Persistent variables in python

P: n/a
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:
>>doStuff()
First call!
Function call!
>>doStuff()
Function call!
>>doStuff()
Function call!
>>doStuff()
Function call!
>>doStuff.timesUsed
4

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

/buffi (www.buffis.com)

Dec 26 '06 #1
Share this Question
Share on Google+
9 Replies


P: n/a
At Tuesday 26/12/2006 19:13, buffi wrote:
>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

Dec 26 '06 #2

P: n/a
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)

Dec 26 '06 #3

P: n/a
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.
>>@static(foo=5)
... def bar():
... print bar.foo
... bar.foo += 1
...
>>bar()
5
>>bar()
6
'''

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

Dec 26 '06 #4

P: n/a
On Tue, 26 Dec 2006 15:01:40 -0800, buffi wrote:

>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.
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

--
Steven.

Dec 27 '06 #5

P: n/a
"buffi" <bj**********@gmail.comwrote:
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
>>fn = dostuff()
fn.next()
First call!
Function call 1
1
>>fn.next()
Function call 2
2
>>fn.next()
Function call 3
3
>>timesused = fn.next()
Function call 4
>>print timesused
4
Dec 27 '06 #6

P: n/a
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

Dec 27 '06 #7

P: n/a
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.

Dec 27 '06 #8

P: n/a
At Tuesday 26/12/2006 21:06, Steven D'Aprano wrote:
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
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

Dec 28 '06 #9

P: n/a
On Wed, 27 Dec 2006 10:11:11 -0800, ed************@gmail.com wrote:
>
>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.
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.

--
Steven D'Aprano

Dec 28 '06 #10

This discussion thread is closed

Replies have been disabled for this discussion.