replace mothod for only one object but not for a class

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

    #1

    replace mothod for only one object but not for a class

    Hi,

    I have multiple objects all belonging to the same class
    (which I didn't implement and whose code I don't want to modify)

    Now I'd like to change one method for one object only (after it has
    been created) without adding any overhead
    to the call of the other object's methods.


    Is this possible?

    Example
    ##### This is NOT what I'd like to do
    ##### as it overwrites the method for all objects of this class
    o1 = aclass()
    o2 = aclass()
    # call original method
    o1.method()
    o2.method()
    # overwrite the method for the entire class
    aclass.method = mymethod
    o1.method() # now new method
    o2.method() # now new method


    ####### What doesn't work, but what I'd like to do
    o1 = aclass()
    o2 = aclass()
    # call original method
    o1.method()
    o2.method()
    # overwrite the method for the entire class
    o1.method = mymethod
    o1.method() # now new method
    o2.method() # still old method


    thanks for any pointers.


    P.S. I guess, that there is a computer science term for what I try to
    achieve.
    If anybody knew it I would be interested to learn it as well.

  • Miki

    #2
    Re: replace mothod for only one object but not for a class

    from functools import partial

    class Point:
    def __init__(self, x, y):
    self.x, self.y = x, y

    def show(self, n):
    for i in range(n):
    print "Point: (%s, %s)" % (self.x, self.y)

    def new_method(obj, func):
    def method(*args, **kw):
    return func(obj, *args, **kw)

    return method


    p1 = Point(1, 2)
    p2 = Point(3, 4)

    def show(self, n):
    print "Not a Point: %s-%s" % (self.x, self.y)
    p2.show = partial(show, p2)

    p1.show(3)
    p2.show(3)

    HTH,
    --
    Miki <miki.tebeka@gm ail.com>
    If it won't be simple, it simply won't be. [Hire me, source code]

    Comment

    • George Sakkis

      #3
      Re: replace mothod for only one object but not for a class

      On Oct 14, 1:50 pm, hofer <bla...@dungeon .dewrote:
      Hi,
      >
      I have multiple objects all belonging to the same class
      (which I didn't implement and whose code I don't want to modify)
      >
      Now I'd like to change one method for one object only (after it has
      been created) without adding any overhead
      to the call of the other object's methods.
      >
      Is this possible?
      >
      Example
      ##### This is NOT what I'd like to do
      ##### as it overwrites the method for all objects of this class
      o1 = aclass()
      o2 = aclass()
      # call original method
      o1.method()
      o2.method()
      # overwrite the method for the entire class
      aclass.method = mymethod
      o1.method() # now new method
      o2.method() # now new method
      >
      ####### What doesn't work, but what I'd like to do
      o1 = aclass()
      o2 = aclass()
      # call original method
      o1.method()
      o2.method()
      # overwrite the method for the entire class
      o1.method = mymethod
      o1.method() # now new method
      o2.method() # still old method
      >
      thanks for any pointers.
      Please post the actual code that doesn't work. The following works as
      expected:
      >>class A(object):
      ... def foo(self): return 'Original'
      ...
      >>a = A()
      >>b = A()
      >>a.foo()
      'Original'
      >>b.foo()
      'Original'
      >>b.foo = lambda: 'Modified'
      >>a.foo()
      'Original'
      >>b.foo()
      'Modified'


      HTH,
      George

      Comment

      • Jason Scheirer

        #4
        Re: replace mothod for only one object but not for a class

        On Oct 14, 11:20 am, George Sakkis <george.sak...@ gmail.comwrote:
        On Oct 14, 1:50 pm, hofer <bla...@dungeon .dewrote:
        >
        >
        >
        Hi,
        >
        I have multiple objects all belonging to the same class
         (which I didn't implement and whose code I don't want to modify)
        >
        Now I'd like to change one method for one object only (after it has
        been created) without adding any overhead
        to the call of the other object's methods.
        >
        Is this possible?
        >
        Example
        ##### This is NOT what I'd like to do
        ##### as it overwrites the method for all objects of this class
        o1 = aclass()
        o2 = aclass()
        # call original method
        o1.method()
        o2.method()
        # overwrite the method for the entire class
        aclass.method = mymethod
        o1.method() # now new method
        o2.method() # now new method
        >
        ####### What doesn't work, but what I'd like to do
        o1 = aclass()
        o2 = aclass()
        # call original method
        o1.method()
        o2.method()
        # overwrite the method for the entire class
        o1.method = mymethod
        o1.method() # now new method
        o2.method() # still old method
        >
        thanks for any pointers.
        >
        Please post the actual code that doesn't work. The following works as
        expected:
        >
            >>class A(object):
            ...     def foo(self): return 'Original'
            ...
            >>a = A()
            >>b = A()
            >>a.foo()
            'Original'
            >>b.foo()
            'Original'
            >>b.foo = lambda: 'Modified'
            >>a.foo()
            'Original'
            >>b.foo()
            'Modified'
        >
        HTH,
        George
        What you're doing is called monkeypatching. I consider it dangerous,
        but to each his own. Python's metaprogramming facilities can handle
        it.

        The lambda approach can leak, I've had garbage collection issues when
        shuffling around bound methods (my objects weren't being collected
        when expected, basically). The partial approach is cool, too, but it
        won't reliably work with things like __getattr__ and __setattr__,
        which I also learned the hard way. The probable best way of going
        about it is to define a new class that implements your one method, and
        use some on-the-fly magic to create a NEW class that inherits from
        both your monkeypatch class and your current instance's class:

        In [2]: class main_implementa tion(object):
        ...: def a(self):
        ...: print 'a'
        ...: def b(self, argb='b'):
        ...: print 'B says %r' % argb
        ...:
        ...:

        In [6]: class new_implementat ion_of_a(object ):
        ...: def a(self):
        ...: print "This is a new implementation of A"
        ...:
        ...:

        In [7]: mymain = main_implementa tion()

        In [8]: mymain.a()
        a

        In [9]: mymain.__class_ _ = type(mymain.__c lass__.__name__ +'MODIFIED',
        (new_implementa tion_of_a, mymain.__class_ _), {})

        In [10]: mymain
        Out[10]: <__main__.main_ implementationM ODIFIED object at 0x0137EA50>

        In [11]: mymain.a()
        This is a new implementation of A

        In [12]: mymain.b()
        B says 'b'

        The magic here occurs when you create a new class on-the-fly using the
        type() built-in function and assign it to the instance's __class__
        attribute.

        Good luck.

        Comment

        • Christian Heimes

          #5
          Re: replace mothod for only one object but not for a class

          Bruno Desthuilliers wrote:
          If the class is a new-style one [1], it just requires invoking the
          descriptor protocol by yourself to get a bound method, ie:
          Another note about new style classes:
          You can NOT overwrite most magic methods (__*__) on the instance. Most
          magic methods are only looked up on the class object.

          Christian

          Comment

          • George Sakkis

            #6
            Re: replace mothod for only one object but not for a class

            On Oct 14, 12:28 pm, Bruno Desthuilliers
            <bdesth.quelque ch...@free.quel quepart.frwrote :
            George Sakkis a écrit :
            >
            >
            >
            On Oct 14, 1:50 pm, hofer <bla...@dungeon .dewrote:
            Hi,
            >
            I have multiple objects all belonging to the same class
            (which I didn't implement and whose code I don't want to modify)
            >
            Now I'd like to change one method for one object only (after it has
            been created) without adding any overhead
            to the call of the other object's methods.
            >
            Is this possible?
            >
            Example
            ##### This is NOT what I'd like to do
            ##### as it overwrites the method for all objects of this class
            o1 = aclass()
            o2 = aclass()
            # call original method
            o1.method()
            o2.method()
            # overwrite the method for the entire class
            aclass.method = mymethod
            o1.method() # now new method
            o2.method() # now new method
            >
            ####### What doesn't work, but what I'd like to do
            o1 = aclass()
            o2 = aclass()
            # call original method
            o1.method()
            o2.method()
            # overwrite the method for the entire class
            o1.method = mymethod
            o1.method() # now new method
            o2.method() # still old method
            >
            thanks for any pointers.
            >
            Please post the actual code that doesn't work. The following works as
            expected:
            >
            >>class A(object):
            ... def foo(self): return 'Original'
            ...
            >>a = A()
            >>b = A()
            >>a.foo()
            'Original'
            >>b.foo()
            'Original'
            >>b.foo = lambda: 'Modified'
            >>a.foo()
            'Original'
            >>b.foo()
            'Modified'
            >
            Except that b.foo is a plain function, not a method:
            >
            >>class A(object):
            ... def foo(self): return "original called on %s" % self
            ...
            >>a = A()
            >>b = A()
            >>a.foo()
            'original called on <__main__.A object at 0xb7c0c56c>'
            >>b.foo()
            'original called on <__main__.A object at 0xb7c0c4ec>'
            >>b.foo = lambda: "modified called on %s" % self
            >>b.foo
            <function <lambdaat 0xb7bfe9cc>
            >>b.foo()
            Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
            File "<stdin>", line 1, in <lambda>
            NameError: global name 'self' is not defined
            >>b.foo = lambda self: "modified called on %s" % self
            >>b.foo()
            Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
            TypeError: <lambda>() takes exactly 1 argument (0 given)
            >>>
            You're right of course; that's what you get with minimal testing ;)
            Still it works with a small modification, binding self to b as default
            argument:

            b.foo = lambda self=b: "modified called on %s" % self

            The functools.parti al() solution shown before is the generalization of
            this for more than one arguments. So I'm wondering, except for the
            callable's exact type (bound method vs function vs functools.parti al
            object) is there any other difference and a reason to prefer one over
            the other ? If not, functools.parti al() seems more readable than
            calling explicitly the descriptor.

            George

            Comment

            • Bruno Desthuilliers

              #7
              Re: replace mothod for only one object but not for a class

              George Sakkis a écrit :
              (snip)
              You're right of course; that's what you get with minimal testing ;)
              Still it works with a small modification, binding self to b as default
              argument:
              >
              b.foo = lambda self=b: "modified called on %s" % self
              Ok, now with a real use case : use a named function instead of a lambda,
              and define the function before having access to b !-)
              The functools.parti al() solution shown before is the generalization of
              this for more than one arguments. So I'm wondering, except for the
              callable's exact type (bound method vs function vs functools.parti al
              object) is there any other difference and a reason to prefer one over
              the other ?
              Consistency ? Possibly performances (NB : not benchmarked - might be
              worth a little homework to check this out) ?


              If not, functools.parti al() seems more readable than
              calling explicitly the descriptor.
              Perhaps is it just me starting to getting senile and resistant to new
              ideas, but while it does indeed work, I see the partial() solution as a
              WTF. Invoking function.__get_ _ *is* the obvious[1] way to get a bound
              method from a function.

              [1] for anyone that know how Python turns functions into methods, that is...

              But indeed, methods are specialized partial applications, and your
              solution is perfectly valid.

              Comment

              • Bruno Desthuilliers

                #8
                Re: replace mothod for only one object but not for a class

                Bruno Desthuilliers a écrit :
                hofer a écrit :
                >Hi,
                >>
                >I have multiple objects all belonging to the same class
                > (which I didn't implement and whose code I don't want to modify)
                >>
                >Now I'd like to change one method for one object only (after it has
                >been created) without adding any overhead
                >to the call of the other object's methods.
                >>
                >>
                >Is this possible?
                >
                Yes.
                >
                If the class is a new-style one [1], it just requires invoking the
                descriptor protocol by yourself to get a bound method, ie:
                >
                (snip)

                If it's a classic class, you can get by using either new.instancemet hod
                or types.MethodTyp e:
                Either I'm starting to get old or I need some vacations... Manually
                invoking function.__get_ _(obj, type(obj)) also work with classic
                classes, of course.

                (snip)

                Comment

                • hofer

                  #9
                  Re: replace mothod for only one object but not for a class

                  On Oct 14, 7:50 pm, hofer <bla...@dungeon .dewrote:
                  Hi,
                  >
                  I have multiple objects all belonging to the same class
                   (which I didn't implement and whose code I don't want to modify)
                  >
                  Now I'd like to change one method for one object only (after it has
                  been created) without adding any overhead
                  to the call of the other object's methods.
                  >
                  Is this possible?
                  >
                  Example
                  ##### This is NOT what I'd like to do
                  ##### as it overwrites the method for all objects of this class
                  o1 = aclass()
                  o2 = aclass()
                  # call original method
                  o1.method()
                  o2.method()
                  # overwrite the method for the entire class
                  aclass.method = mymethod
                  o1.method() # now new method
                  o2.method() # now new method
                  >
                  ####### What doesn't work, but what I'd like to do
                  o1 = aclass()
                  o2 = aclass()
                  # call original method
                  o1.method()
                  o2.method()
                  # overwrite the method for the entire class
                  o1.method = mymethod
                  o1.method() # now new method
                  o2.method() # still old method
                  >
                  thanks for any pointers.
                  >
                  P.S. I guess, that there is a computer science term for what I try to
                  achieve.
                  If anybody knew it I would be interested to learn it as well.

                  Thanks a lot this works.
                  Though I must admint, that even after reading the doc I don't really
                  understand why.

                  Comment

                  • hofer

                    #10
                    Re: replace mothod for only one object but not for a class

                    Hi
                    hofer a écrit :
                    I have multiple objects all belonging to the same class
                    (which I didn't implement and whose code I don't want to modify)
                    >
                    Now I'd like to change one method for one object only (after it has
                    been created) without adding any overhead
                    to the call of the other object's methods.
                    Thanks for all of your answers:

                    Here an example with three of the suggested solutions:
                    (I didn't succeed in implementing Jason's solution with my
                    example)

                    ############### ############### ############### ###########
                    import threading
                    # some objects
                    a = threading.Event ()
                    b = threading.Event ()
                    c = threading.Event ()
                    d = threading.Event ()

                    def run_dly(o): # a test function
                    print o,"start",
                    o.wait(1)
                    print "stop"

                    # unmodified test
                    run_dly(a)
                    run_dly(b)
                    run_dly(c)
                    run_dly(d)

                    # The new Method
                    def verbose_wait(se lf,dly):
                    print "VERBOSE",
                    threading._Even t.wait(self,dly )

                    ### Implemented with partial
                    from functools import partial
                    b.wait = partial(verbose _wait,b)

                    ### with __get__ for new classes
                    c.wait = verbose_wait.__ get__(c,type(c) )

                    ## with new for old classes
                    import new
                    d.wait = new.instancemet hod(verbose_wai t,d,type(d))

                    run_dly(a)
                    run_dly(b)
                    run_dly(c)
                    run_dly(d)
                    ############# end
                    thanks again

                    Hofer

                    Comment

                    • Bruno Desthuilliers

                      #11
                      Re: replace mothod for only one object but not for a class

                      hofer a écrit :
                      Hi
                      >
                      >>hofer a écrit :
                      >>>I have multiple objects all belonging to the same class
                      >>> (which I didn't implement and whose code I don't want to modify)
                      >>>Now I'd like to change one method for one object only (after it has
                      >>>been created) without adding any overhead
                      >>>to the call of the other object's methods.
                      >
                      Thanks for all of your answers:
                      >
                      Here an example with three of the suggested solutions:
                      (I didn't succeed in implementing Jason's solution with my
                      example)
                      >
                      ############### ############### ############### ###########
                      import threading
                      # some objects
                      a = threading.Event ()
                      b = threading.Event ()
                      c = threading.Event ()
                      d = threading.Event ()
                      >
                      def run_dly(o): # a test function
                      print o,"start",
                      o.wait(1)
                      print "stop"
                      >
                      # unmodified test
                      run_dly(a)
                      run_dly(b)
                      run_dly(c)
                      run_dly(d)
                      >
                      # The new Method
                      def verbose_wait(se lf,dly):
                      print "VERBOSE",
                      threading._Even t.wait(self,dly )
                      Note that given your use case, you could have used a decorator here
                      instead...

                      def verbose(method) :
                      def _verbose(*args, **kw):
                      print "%s called on %s" % (method.__name_ _, method.im_self)
                      print "args : ", args, " - kwargs : ", kw
                      return method(*args, **kw)

                      _verbose.__name __ = "verbose wrapper for %s" % method.__name__
                      return _verbose

                      b.wait = verbose(b.wait)



                      Or if you want a more extensible - and "reversible " - solution:


                      class VerboseMethod(o bject):
                      def __init__(self, method, before=None, after=None):
                      # we only want bound methods here
                      obj = getattr(method, "im_self", None)
                      if obj is None:
                      err = "%s expected a bound method, got %s" % (
                      type(self), method
                      )
                      raise ValueError(err)

                      self._method = method
                      self._before = before
                      self._after = after

                      def _verbose_before (self, *args, **kw):
                      """
                      You subclass VerboseMethod and taylor this to your own needs,
                      or alternatively pass a 'before' callback to VerboseMethod
                      that will get called with method, *args, **kw
                      """
                      if callable(self._ before):
                      self._before(se lf._method, *args, **kw)
                      return
                      # default
                      m = self._method
                      print "%s about to be called on %s" % (m.__name__, m.im_self)
                      print "args : ", args, " - kwargs : ", kw

                      def _verbose_after( self, result, *args, **kw):
                      """
                      You subclass VerboseMethod and taylor this to your own needs,
                      or alternatively pass an 'after' callback to VerboseMethod
                      that will get called with method, result, *args, **kw
                      """
                      if callable(self._ after):
                      self._after(sel f._method, result, *args, **kw)
                      return
                      # default
                      m = self._method
                      print "%s called on %s" % (m.__name__, m.im_self)
                      print "args : ", args, " - kwargs : ", kw
                      print "result : ", result

                      def __call__(self, *args, **kw):
                      self._verbose_b efore(*args, **kw)
                      result = self._method(*a rgs, **kw)
                      self._verbose_a fter(result, *args, **kw)
                      return result

                      def drop(self):
                      """restore the original method..."""
                      obj = self._method.im _self
                      delattr(obj, self._method.__ name__)



                      class B(object):
                      def __init__(self, name):
                      self.name = name

                      def wait(self, dly=42):
                      return "%s.wait(%s )" % (self.name, dly)

                      b1 = B('b1')
                      b2 = B('b2')

                      print b1.wait()
                      print b2.wait()

                      b1.wait = VerboseMethod(b 1.wait)
                      print b1.wait()
                      print b2.wait()

                      b1.wait.drop()
                      print b1.wait()
                      print b2.wait()

                      def before(m, *args, **kw):
                      print "test before"
                      print m, args, kw


                      def after(m, r, *args, **kw):
                      print "test after"
                      print m, r, args, kw

                      b1.wait = VerboseMethod(b 1.wait, before=before, after=after)
                      print b1.wait()
                      print b2.wait()

                      b1.wait.drop()
                      print b1.wait()
                      print b2.wait()


                      HTH

                      Comment

                      Working...