10
  • I have an external library which I cannot touch. This library has a function, genA(), that returns the instance of class A.
  • In my side I define class B as a subclass of class A.
  • I want to use the instance of class B in my project, but the instance should be generated by genA().

Is there any standard and easy way to do this?


# I cannnot tweak these code

def genA():
    a = A
    return(a)

class A:
    def __init__():
        self.a = 1

# ---

# code in my side

class B(A):
    def __init__():
        self.b = 2


a = genA()
# like a copy-constructor, doesn't work
# b = B(a)

# I want to get this
b.a # => 1
b.b # => 2

Here is an equivalent c++ code:

#include <iostream>

// library side code
class A {
public:
  int a;
  // ... many members

  A() { a = 1; }
};

void fa(A a) {
  std::cout << a.a << std::endl;
}

A genA() { A a; return a; }

// ///
// my code

class B : public A {
public:
  int b;
  B() : A() { init(); }
  B(A& a) : A(a) { init(); }
  void init() { b = 2; }
};

void fb(B b) {
  std::cout << b.b << std::endl;
}


int main(void) {
  A a = genA();
  B b(a);

  fa(b); // => 1
  fb(b); // => 2
}
kohske
  • 65,572
  • 8
  • 165
  • 155
  • This is sort of possible through extremely kludgy methods, but I wouldn't recommend it. Can't you stick the `A` object in some sort of wrapper instead of trying to change its class? – user2357112 Mar 25 '15 at 23:35
  • Thanks, actually I need pass the object to both library-side functions and my functions, so the object should be instance of A in some sense. But now I understand the difficulty (surprising for me!), I'll take another approach. Perhaps define class B that has a class A instance as its member. And when I need to pass it to library-side function, call, say, `a = genA(); B b; b.a_instance =a; lib_fun(b.a_instance)`. Do you think it makes sense? – kohske Mar 25 '15 at 23:43
  • I'm not sure how well it'll work, but it seems feasible. – user2357112 Mar 25 '15 at 23:48

3 Answers3

11

You shouldn't do it with __new__, but it works only with new-style classes:

class A(object):
    def __init__(self):
        self.a = 10

class B(A):
    def __new__(cls, a):
        a.__class__ = cls
        return a

    def __init__(self, a):
        self.b = 20

a = A()
b = B(a)

print type(b), b.a, b.b   # <class '__main__.B'> 10 20

But as I said, don't do that, you should probably use aggregation, not a subclassing in such cases. If you wish to make B so it will have same interface as A, you may write transparent proxy with __getattr__:

class B(object):
    def __init__(self, a):
        self.__a = a
        self.b = 20

    def __getattr__(self, attr):
        return getattr(self.__a, attr)

    def __setattr__(self, attr, val):
        if attr == '_B__a':
            object.__setattr__(self, attr, val)

        return setattr(self.__a, attr, val)
myaut
  • 11,174
  • 2
  • 30
  • 62
  • 1
    Thanks, if class A has a lot of members, do I need write a setter/getter for all members...? – kohske Mar 26 '15 at 00:10
  • 1
    @kohske, no, you don't. Each time you try to get `b.a`, Python can't find `a` attribute, so it ask your `__getattr__` to help, and passes `"a"` as `attr` argument, so `__getattr__` handles all attributes of `A` class. – myaut Mar 26 '15 at 07:43
  • 1
    Thanks, it's useful. May I ask why I shouldn't do with __new__? – kohske Mar 26 '15 at 09:32
  • 1
    @kohske: redefining `__class__` is working but very unusual technique. It will confuse other developers, and may lead to unforeseen consequences (i.e. in multiple-inheritance, you may also need to override MRO). Also, it doesn't match any design pattern I know. – myaut Mar 26 '15 at 09:55
  • @myaut, shouldn't it be `def __setattr__(self, attr, val): if attr == '_B__a': object.__setattr__(self, attr, val); else: return setattr(self.__a, attr, val)`; Otherwise won't Python look for a variable called `_B__a` inside of self.__a? – Nick Crews Mar 01 '17 at 01:49
2

I dont understand your code. IMO its incorrect. First, there is no self in __init__ in both A and B. Second, in your B class you are not calling A's constructor. Third, genA does not return any objects, just reference to A class. Please check changed code:

def genA():
    a = A() #<-- need ()
    return(a)

class A:
    def __init__(self):  # <-- self missing
        self.a = 1

# ---

# code in my side

class B(A):
    def __init__(self, a=None):
        super().__init__()  #<-- initialize base class

        if isinstance(a, A): #<-- if a is instance of base class, do copying
            self.a = a.a

        self.b = 2


a = genA()
a.a = 5
b = B(a)

# this works
print(b.a) # => 5
print(b.b) # => 2   
Marcin
  • 215,873
  • 14
  • 235
  • 294
  • Thanks, if class A has a lot of members, do I need write `self.a = a.a` for all members? – kohske Mar 26 '15 at 00:10
  • Short answer yes. Long answer, you could [iterate through the variables in a loop](http://stackoverflow.com/questions/11637293/iterate-over-object-attributes-in-python). But I think this is out of the scope of this question. – Marcin Mar 26 '15 at 00:18
  • Really thanks. It's easy to use iteration, but I just wanted to know if there is an easy and standard way. So thanks a lot. – kohske Mar 26 '15 at 00:22
0

There doesn't seem to be a standard way of doing it, but there are several approaches. If you didn't want to take care of each individual attribute, I'd suggest the following one:

class A(object):
    # Whatever

class B(A):
    def __init__(self, a):
        super(B, self).__init__()
        for attr in dir(a):
            setattr(self, attr, getattr(a, attr))
        # Any other specific attributes of class B can be set here

# Now, B can be instantiated this way:
a = A()
b = B(a)

In case you didn't want to access the parent's "private" attributes, you could add

if not attr.startswith('__'):

at the for loop.

LostMyGlasses
  • 3,074
  • 20
  • 28