using "private" parameters as static storage?
J. Cliff Dyer
jcd at sdf.lonestar.org
Thu Nov 13 13:15:04 EST 2008
On Thu, 2008-11-13 at 11:19 -0600, Chris Mellon wrote:
> On Thu, Nov 13, 2008 at 11:16 AM, Joe Strout <joe at strout.net> wrote:
> > One thing I miss as I move from REALbasic to Python is the ability to have
> > static storage within a method -- i.e. storage that is persistent between
> > calls, but not visible outside the method. I frequently use this for such
> > things as caching, or for keeping track of how many objects a factory
> > function has created, and so on.
> >
> > Today it occurred to me to use a mutable object as the default value of a
> > parameter. A simple example:
> >
> > def spam(_count=[0]):
> > _count[0] += 1
> > return "spam " * _count[0]
> >
> >>>> spam()
> > 'spam '
> >>>> spam()
> > 'spam spam '
> >
> > This appears to work fine, but it feels a little unclean, having stuff in
> > the method signature that is only meant for internal use. Naming the
> > parameter with an underscore "_count" makes me feel a little better about
> > it. But then, adding something to the module namespace just for use by one
> > function seems unclean too.
> >
> > What are your opinions on this idiom? Is there another solution people
> > generally prefer?
> >
> > Ooh, for a change I had another thought BEFORE hitting Send rather than
> > after. Here's another trick:
> >
> > def spam2():
> > if not hasattr(spam2,'count'):spam2.count=0
> > spam2.count += 1
> > return "spam2 " * spam2.count
> >
> > This doesn't expose any uncleanliness outside the function at all. The
> > drawback is that the name of the function has to appear several times within
> > itself, so if I rename the function, I have to remember to change those
> > references too. But then, if I renamed a function, I'd have to change all
> > the callers anyway. So maybe this is better. What do y'all think?
> >
>
> Static storage is a way of preserving state. Objects are a way of
> encapsulating state and behavior. Use an object.
He is using an object. Specifically, he's using a function object.
Though perhaps you meant put it into a class.
Here are a few essays into the matter
>>> def foo():
... foo._count += 1
... return ("spam " * foo.count).rstrip()
...
>>> foo._count=0
>>> foo()
'spam'
>>> foo()
'spam spam'
>>>
Simple and straightforward, and _count is still encapsulated in the
function, but it's kind of ugly, because when the function has been
defined, it is not functional.
Attempt #2 -- put it in a decorator
>>> def add_counter(f):
... f._count = 0
... return f
...
>>> @add_counter
... def foo():
... foo._count += 1
... return ("spam " * foo._count).rstrip()
>>> foo()
'spam'
>>> foo()
'spam spam'
>>>
Now it's complete as soon as the function is defined, but the decorator
is tightly bound to the function, in its use of _count. I find that
kind of ugly. This is the first decorator I've written that doesn't
define a function inside itself.
Try three. Let's put it in a class:
>>> class Foo(object):
... def __init__(self, counter_start=0):
... self._count = counter_start
... def __call__(self):
... self._count += 1
... return ("spam " * self._count).rstrip()
...
>>> foo = Foo()
>>> foo()
'spam'
>>> foo()
'spam spam'
>>>
Essentially, this has the same behavior as the other two. But it looks
cleaner, and you don't have to worry about coupling separate functions,
or refering to a function by name within itself (because you have self
to work with). But the "object" semantics are essentially the same, and
state is just as legitimately preserved on a function object as a Foo
object.
Cheers,
Cliff
More information about the Python-list
mailing list