3

Suppose I have a class A coming from a third-party library (FYI it is numpy.matrix). I want to expand its public interface with a wrapper class B.

class B(A):
    def myMethod(self,arg):
        return 'spam'

And also, I want to create B objects from A object. Since B will not hold any more attribute, I just really want to return the A object, with the B behavior:

class B(A):
    def __new__(cls,a): # type(a)==A
        return a.castTo(B) # pseudo-code

Is there a way to do it in python ?

Similarily, is there a way to cast B objects to A objects ?

Bérenger
  • 2,678
  • 2
  • 21
  • 42
  • Related: http://stackoverflow.com/questions/25394550/adding-methods-to-an-existing-class-instance-or-how-to-subclass-an-instance – Nick T Oct 05 '14 at 03:51
  • 1
    @NickT: Thank you. It seems to be sooo complicated. Isn't the language supporting this kind of basic OOP stuff ? In C++ it would be a short one-liner. Same in ruby (AFAIK). Or Python does not work this way ? I am confused... – Bérenger Oct 05 '14 at 04:10
  • Seems like you should be able to use a decorator: http://thecodeship.com/patterns/guide-to-python-function-decorators/ – user4107395 Oct 05 '14 at 04:55

1 Answers1

2

Your class B's __new__ needs to call A's __new__. Here's a brief demo using the built-in list class.

class mylist(list):
    def __new__(cls, obj=None):
        return list.__new__(cls, obj)

    def mymethod(self, msg):
        print msg, id(self)


def main():
    a = mylist('abc')
    print a, id(a)
    a.mymethod('hello')


if __name__ == '__main__':
    main()

If you just want to add a simple function to an instance of an existing class, you can do this sort of thing.

class A(object):
    pass

def silly(arg=None):
    print 'This is silly', arg

b = A()
b.my = silly
b.my()

Note that b.my it just a function, not a method, so it does not get passed self when you call it. But you can pass it "by hand" by doing b.my(b)


Edit

As an alternative to writing list.__new__(cls, obj), you can call the super() function:

def __new__(cls, obj=None):
    return super(mylist, cls).__new__(cls, obj)

But in most cases using the base class name is clearer and shorter. I guess there might be a reason to prefer super(), but I'm not enough of an expert on this stuff to say. :)

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Thank you, the first part is the answer I expected. I wasn't able to find anything on the internet. I used __new_ to return [], not list.__new__(cls, obj), so I ended up with a base class object. – Bérenger Oct 05 '14 at 22:33
  • I learned about that syntax from Guido, in [the docs](https://www.python.org/download/releases/2.2.3/descrintro#__new__), but it's not something I've used often. – PM 2Ring Oct 06 '14 at 12:56
  • Note that the `super()` calling syntax has been simplified in Python 3. https://docs.python.org/3/library/functions.html?super#super – PM 2Ring Jul 12 '22 at 01:32