Decorating class member functions

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Andy Terrel

    #1

    Decorating class member functions

    Okay does anyone know how to decorate class member functions?

    The following code gives me an error:

    Traceback (most recent call last):
    File "decorators2.py ", line 33, in <module>
    s.update()
    File "decorators2.py ", line 13, in __call__
    retval = self.fn.__call_ _(*args,**kws)
    TypeError: update() takes exactly 1 argument (0 given)

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


    #! /usr/bin/env python

    class Bugger (object):
    def __init__ (self, module, fn):
    self.module = module
    self.fn = fn

    def __call__ (self,*args, **kws):
    ret_val = self.fn(*args,* *kws)
    return ret_val

    def instrument (module_name):
    ret_val = lambda x: Bugger(module_n ame, x)
    return ret_val

    class Stupid:
    def __init__(self):
    self.val = 1

    @instrument("xp d.spam")
    def update(self):
    self.val += 1


    s = Stupid()
    s.update()

  • Andy Terrel

    #2
    Re: Decorating class member functions

    Oh I should mention the decorator needs to have some notion of state
    (such as with the above class)

    Comment

    • Virgil Dupras

      #3
      Re: Decorating class member functions

      On May 3, 9:21 pm, Andy Terrel <andy.ter...@gm ail.comwrote:
      Okay does anyone know how to decorate class member functions?
      >
      The following code gives me an error:
      >
      Traceback (most recent call last):
      File "decorators2.py ", line 33, in <module>
      s.update()
      File "decorators2.py ", line 13, in __call__
      retval = self.fn.__call_ _(*args,**kws)
      TypeError: update() takes exactly 1 argument (0 given)
      >
      ------------------
      >
      #! /usr/bin/env python
      >
      class Bugger (object):
      def __init__ (self, module, fn):
      self.module = module
      self.fn = fn
      >
      def __call__ (self,*args, **kws):
      ret_val = self.fn(*args,* *kws)
      return ret_val
      >
      def instrument (module_name):
      ret_val = lambda x: Bugger(module_n ame, x)
      return ret_val
      >
      class Stupid:
      def __init__(self):
      self.val = 1
      >
      @instrument("xp d.spam")
      def update(self):
      self.val += 1
      >
      s = Stupid()
      s.update()
      A decorator is a function that takes one single parameter: a function.
      "instrument " must return a decorator.

      Comment

      • Virgil Dupras

        #4
        Re: Decorating class member functions

        On May 3, 9:33 pm, Virgil Dupras <hardcoded.soft w...@gmail.comw rote:
        On May 3, 9:21 pm, Andy Terrel <andy.ter...@gm ail.comwrote:
        >
        >
        >
        Okay does anyone know how to decorate class member functions?
        >
        The following code gives me an error:
        >
        Traceback (most recent call last):
        File "decorators2.py ", line 33, in <module>
        s.update()
        File "decorators2.py ", line 13, in __call__
        retval = self.fn.__call_ _(*args,**kws)
        TypeError: update() takes exactly 1 argument (0 given)
        >
        ------------------
        >
        #! /usr/bin/env python
        >
        class Bugger (object):
        def __init__ (self, module, fn):
        self.module = module
        self.fn = fn
        >
        def __call__ (self,*args, **kws):
        ret_val = self.fn(*args,* *kws)
        return ret_val
        >
        def instrument (module_name):
        ret_val = lambda x: Bugger(module_n ame, x)
        return ret_val
        >
        class Stupid:
        def __init__(self):
        self.val = 1
        >
        @instrument("xp d.spam")
        def update(self):
        self.val += 1
        >
        s = Stupid()
        s.update()
        >
        A decorator is a function that takes one single parameter: a function.
        "instrument " must return a decorator.
        Oh wait, I just embarrassed myself. Nevermind my last post.

        Comment

        • Virgil Dupras

          #5
          Re: Decorating class member functions

          On May 3, 9:21 pm, Andy Terrel <andy.ter...@gm ail.comwrote:
          Okay does anyone know how to decorate class member functions?
          >
          The following code gives me an error:
          >
          Traceback (most recent call last):
          File "decorators2.py ", line 33, in <module>
          s.update()
          File "decorators2.py ", line 13, in __call__
          retval = self.fn.__call_ _(*args,**kws)
          TypeError: update() takes exactly 1 argument (0 given)
          >
          ------------------
          >
          #! /usr/bin/env python
          >
          class Bugger (object):
          def __init__ (self, module, fn):
          self.module = module
          self.fn = fn
          >
          def __call__ (self,*args, **kws):
          ret_val = self.fn(*args,* *kws)
          return ret_val
          >
          def instrument (module_name):
          ret_val = lambda x: Bugger(module_n ame, x)
          return ret_val
          >
          class Stupid:
          def __init__(self):
          self.val = 1
          >
          @instrument("xp d.spam")
          def update(self):
          self.val += 1
          >
          s = Stupid()
          s.update()
          Second attempt. After some fuss around, I think it's just not possible
          to have a class instance as a decorator. the self gets lost in
          translation.

          #! /usr/bin/env python
          class Caller(object):
          def __init__ (self, fn):
          self.fn = fn

          def __call__(self, *args, **kwargs):
          print 'Caller calling!', repr(args)
          return self.fn(*args, **kwargs)

          def mydecorator(f):
          return Caller(f)

          class Stupid:
          def __init__(self):
          self.val = 1

          @mydecorator
          def update(self):
          self.val += 1

          s = Stupid()
          s.update()

          Caller calling! ()
          Traceback (most recent call last):
          File "/Users/hsoft/Desktop/foo.py", line 22, in ?
          s.update()
          File "/Users/hsoft/Desktop/foo.py", line 8, in __call__
          return self.fn(*args, **kwargs)
          TypeError: update() takes exactly 1 argument (0 given)

          But why do you want to use a class? If you want to have a decorator
          with argument, you only need to have something like:

          def instrument(modu le):
          def decorator(f):
          def wrapper(*args, **kwargs):
          print module
          return f(*args, **kwargs)
          return wrapper
          return decorator

          Comment

          • Andy Terrel

            #6
            Re: Decorating class member functions

            I just need to keep the state around. I make a call to some function
            that is pretty expensive so I want to save it as a member during the
            __init__ of the decorator.

            Yeah I'm afraid it can't be done either, that's why I asked the group.




            Comment

            • Jorge Godoy

              #7
              Re: Decorating class member functions

              Andy Terrel <andy.terrel@gm ail.comwrites:
              I just need to keep the state around. I make a call to some function
              that is pretty expensive so I want to save it as a member during the
              __init__ of the decorator.
              >
              Yeah I'm afraid it can't be done either, that's why I asked the group.
              Have you looked at memoize decorators? They seem to do what you want.
              There are examples at the Python website (some link in there, I'm
              sorry...).

              This will give you lots of resources, including full recipes and
              comments from the Python cookbook:



              --
              Jorge Godoy <jgodoy@gmail.c om>

              Comment

              • Andy Terrel

                #8
                Re: Decorating class member functions

                not quite as elegant but here is a workaround... Thanks Virgil for
                taking some time to think about it.

                ---

                class Bugger (object):
                def __init__ (self, module):
                print "Entering __init__"
                self.module = module
                self.verb = 0

                def instrument (module_name):
                def wrapper(f):
                def _wrap(*args,**k ws):
                ret_val = f(*args,**kws)
                return ret_val
                return _wrap
                b = Bugger(module_n ame)
                if b.verb == 0:
                ret_val = wrapper
                else:
                ret_val = lambda x:x
                return ret_val

                class Stupid:
                def __init__(self):
                self.val = 1

                @instrument("sp am")
                def update(self):
                self.val += 1


                s = Stupid()
                s.update()
                s.update()
                s.update()
                s.update()
                print s.val

                Comment

                • Steven D'Aprano

                  #9
                  Re: Decorating class member functions

                  On Thu, 03 May 2007 19:28:52 -0700, Andy Terrel wrote:
                  I just need to keep the state around. I make a call to some function
                  that is pretty expensive so I want to save it as a member during the
                  __init__ of the decorator.
                  >
                  Yeah I'm afraid it can't be done either, that's why I asked the group.
                  You can do it if you give up on using the decorator syntax.

                  (Now that I've said that, some clever bunny will show me how to do it.)

                  def make_decorator( n):
                  def addspam(fn):
                  def new(*args):
                  print "spam " * n
                  return fn(*args)
                  return new
                  return addspam


                  class Parrot(object):
                  def __init__(self, count=3):
                  from new import instancemethod as im
                  self.describe = im(make_decorat or(count)(self. __class__.descr ibe), self)
                  def describe(self):
                  return "It has beautiful plummage."

                  >>bird = Parrot()
                  >>bird.describe ()
                  spam spam spam
                  'It has beautiful plummage.'
                  >>>
                  >>>
                  >>bird = Parrot(5)
                  >>bird.describe ()
                  spam spam spam spam spam
                  'It has beautiful plummage.'



                  --
                  Steven D'Aprano

                  Comment

                  • Peter Otten

                    #10
                    Re: Decorating class member functions

                    Andy Terrel wrote:
                    Okay does anyone know how to decorate class member functions?
                    >
                    The following code gives me an error:
                    >
                    Traceback (most recent call last):
                    File "decorators2.py ", line 33, in <module>
                    s.update()
                    File "decorators2.py ", line 13, in __call__
                    retval = self.fn.__call_ _(*args,**kws)
                    TypeError: update() takes exactly 1 argument (0 given)
                    >
                    ------------------
                    >
                    >
                    #! /usr/bin/env python
                    >
                    class Bugger (object):
                    def __init__ (self, module, fn):
                    self.module = module
                    self.fn = fn
                    >
                    def __call__ (self,*args, **kws):
                    ret_val = self.fn(*args,* *kws)
                    return ret_val
                    >
                    def instrument (module_name):
                    ret_val = lambda x: Bugger(module_n ame, x)
                    return ret_val
                    >
                    class Stupid:
                    def __init__(self):
                    self.val = 1
                    >
                    @instrument("xp d.spam")
                    def update(self):
                    self.val += 1
                    >
                    >
                    s = Stupid()
                    s.update()
                    The problem is not that you are decorating a method but that you are trying
                    to use a callable class instance as a method. For that to work the class
                    has to implement the descriptor protocol, see



                    class Bugger (object):
                    def __init__ (self, module, fn, instance=None):
                    self.module = module
                    self.fn = fn
                    self.instance = instance

                    def __call__ (self, *args, **kws):
                    print "calling %s.%s()" % (self.module, self.fn.__name_ _)
                    if self.instance is not None:
                    args = (self.instance, ) + args
                    ret_val = self.fn(*args, **kws)
                    return ret_val

                    def __get__(self, instance, class_):
                    if instance is None:
                    return self
                    return Bugger(self.mod ule, self.fn, instance)

                    def instrument (module_name):
                    ret_val = lambda x: Bugger(module_n ame, x)
                    return ret_val

                    class Stupid(object):
                    @instrument("xp d.spam")
                    def update(self):
                    print "update()"

                    s = Stupid()
                    s.update()
                    Stupid.update(s )

                    Peter

                    Comment

                    • 7stud

                      #11
                      Re: Decorating class member functions

                      On May 3, 7:21 pm, Andy Terrel <andy.ter...@gm ail.comwrote:
                      Okay does anyone know how to decorate class member functions?
                      >
                      The following code gives me an error:
                      >
                      Traceback (most recent call last):
                      File "decorators2.py ", line 33, in <module>
                      s.update()
                      File "decorators2.py ", line 13, in __call__
                      retval = self.fn.__call_ _(*args,**kws)
                      TypeError: update() takes exactly 1 argument (0 given)
                      >
                      ------------------
                      >
                      #! /usr/bin/env python
                      >
                      class Bugger (object):
                      def __init__ (self, module, fn):
                      self.module = module
                      self.fn = fn
                      >
                      def __call__ (self,*args, **kws):
                      ret_val = self.fn(*args,* *kws)
                      return ret_val
                      >
                      def instrument (module_name):
                      ret_val = lambda x: Bugger(module_n ame, x)
                      return ret_val
                      >
                      class Stupid:
                      def __init__(self):
                      self.val = 1
                      >
                      @instrument("xp d.spam")
                      def update(self):
                      self.val += 1
                      >
                      s = Stupid()
                      s.update()
                      As far as I can tell, the problem is that the decorator executes when
                      the class is parsed, and at that time there is no self(the instance
                      object). The decorator produces a callable Bugger object, but the
                      callable object has no way to get self when s.update() is called.
                      Normally when you call a class function, like s.update(), the
                      __get__() method in the 'update' function object is called (all
                      function objects have a __get__() method and therefore are
                      descriptors). Then __get__() creates a method object out of the
                      function object(update), and python automatically passes the instance
                      object to the method object. However, the Bugger object does not have
                      a __get__() method, so no method object is created when the Bugger
                      object is called, and therefore self is not automatically passed to
                      the Bugger object.

                      The following solution adds a __get__() method to the Bugger object.
                      Python automatically passes the instance object to the __get__()
                      method, and the solution stores the instance in the Bugger object.
                      Then __call__ is defined to send the instance object to update().

                      class Bugger (object):
                      def __init__ (self, module, fn):
                      self.module = module
                      self.fn = fn

                      def __call__ (self,*args, **kws):
                      ret_val = self.fn(self.ob j, *args,**kws)
                      return ret_val

                      def __get__(descr, inst, instCls=None):
                      descr.obj = inst
                      return descr

                      def instrument (module_name):
                      ret_val = lambda func: Bugger(module_n ame, func)
                      return ret_val

                      class Stupid(object):
                      def __init__(self):
                      self.val = 1

                      @instrument("xp d.spam")
                      def update(self):
                      self.val += 1

                      s = Stupid()
                      s.update()
                      s.update()
                      s.update()
                      print s.val

                      --output:--
                      4

                      Comment

                      • Andy Terrel

                        #12
                        Re: Decorating class member functions

                        Thanks Peter and 7stud. That is the solution that really works for
                        me.

                        Comment

                        • Rhamphoryncus

                          #13
                          Re: Decorating class member functions

                          On May 3, 10:34 pm, Peter Otten <__pete...@web. dewrote:
                          The problem is not that you are decorating a method but that you are trying
                          to use a callable class instance as a method. For that to work the class
                          has to implement the descriptor protocol, see
                          >

                          >
                          class Bugger (object):
                          def __init__ (self, module, fn, instance=None):
                          self.module = module
                          self.fn = fn
                          self.instance = instance
                          >
                          def __call__ (self, *args, **kws):
                          print "calling %s.%s()" % (self.module, self.fn.__name_ _)
                          if self.instance is not None:
                          args = (self.instance, ) + args
                          ret_val = self.fn(*args, **kws)
                          return ret_val
                          >
                          def __get__(self, instance, class_):
                          if instance is None:
                          return self
                          return Bugger(self.mod ule, self.fn, instance)
                          >
                          def instrument (module_name):
                          ret_val = lambda x: Bugger(module_n ame, x)
                          return ret_val
                          >
                          class Stupid(object):
                          @instrument("xp d.spam")
                          def update(self):
                          print "update()"
                          >
                          s = Stupid()
                          s.update()
                          Stupid.update(s )
                          I've been bitten by the "class instances as decorators won't get
                          bound" bug many times. I had no idea there was a direct solution.
                          Thanks!

                          --
                          Adam Olsen, aka Rhamphoryncus

                          Comment

                          Working...