views:

119

answers:

3

I will go straight to the example:

class Foo:
  @execonce
  def initialize(self):
    print 'Called'

>>> f1 = Foo()
>>> f1.initialize()
Called
>>> f1.initialize()
>>> f2 = Foo()
>>> f2.initialize()
Called
>>> f2.initialize()
>>>

I tried to define execonce but could not write one that works with methods.

PS: I cannot define the code in __init__ for initialize has to be called sometime after the object is initialized. cf - cmdln issue 13

+7  A: 
import functools

def execonce(f):

    @functools.wraps(f)
    def donothing(*a, **k):
        pass

    @functools.wraps(f)
    def doit(self, *a, **k):
        try:
            return f(self, *a, **k)
        finally:
            setattr(self, f.__name__, donothing)

    return doit
Alex Martelli
I get `TypeError: donothing() takes at least 1 argument (0 given)`
Sridhar Ratnakumar
Ok, by removing the `self` argument from the `donothing` method .. I was able to make this work.
Sridhar Ratnakumar
I would recommend adding functools.wraps decorator to doit and donothing. This way documentation and other properties of the function will be the same.
Manuel Ceron
@srid, oops, edited to remove the self. @Manuel, yes, let me edit to do that too...
Alex Martelli
A: 

You could do something like this:

class Foo:
  def __init__(self):
    self.initialize_called = False
  def initialize(self):
    if self.initalize_called:
        return
    self.initialize_called = True
    print 'Called'

This is straightforward and easy to read. There is another instance variable and some code required in the __init__ function, but it would satisfy your requirements.

Greg Hewgill
I would like to avoid doing this for every class in my code .. and prefer a decorator or something for the benefit of the DRY principle.
Sridhar Ratnakumar
A: 

try something similar to this

def foo():
     try:
             foo.called
     except:
             print "called"
             foo.called = True

methods and functions are objects. you can add methods and attributes on them. This can be useful for your case. If you want a decorator, just have the decorator allocate the method but first, check the flag. If the flag is found, a null method is returned and consequently executed.

Stefano Borini
This may not work as the method (on which the decorator is called) is not yet bounded to an object .. and `initialize` is to be called once *for each object* .. not all objects.
Sridhar Ratnakumar
hmmmm. you have a point.
Stefano Borini