12
  • 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
}
3
  • 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? Commented Mar 25, 2015 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? Commented Mar 25, 2015 at 23:43
  • I'm not sure how well it'll work, but it seems feasible. Commented Mar 25, 2015 at 23:48

3 Answers 3

13

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)
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, if class A has a lot of members, do I need write a setter/getter for all members...?
@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.
Thanks, it's useful. May I ask why I shouldn't do with new?
@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, 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?
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   

3 Comments

Thanks, if class A has a lot of members, do I need write self.a = a.a for all members?
Short answer yes. Long answer, you could iterate through the variables in a loop. But I think this is out of the scope of this question.
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.
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.