overriding method that returns base class object

S

Stuart McGraw

I have a class A from a third party that I cannot change
and is implemented in C. I derive my own class B from A
and add a couple new methods and override a method. The
problem is that A has a method (call it A.f() ) that creates
and returns a new A object. I need B.f() to return a B
object derived from A.f(). What is the best way to make
that happen?
 
P

Peter Hansen

Stuart said:
I have a class A from a third party that I cannot change
and is implemented in C. I derive my own class B from A
and add a couple new methods and override a method. The
problem is that A has a method (call it A.f() ) that creates
and returns a new A object. I need B.f() to return a B
object derived from A.f(). What is the best way to make
that happen?

If I understand this correctly, it has nothing to do with the
fact that the parent class is implemented in C and you just
need to know a little uncommon syntax:

class A:
def f(self):
return A()

class B(A):
def f(self):
obj = A.f(self)
# do whatever you want to obj here
return obj

The key is what you mean by "a B object derived from A.f()". If
by derived you mean something to do with _inheritance_, then
either you don't understand inheritance or you weren't clear what
you wanted.

If you just mean you want B's f() to do something special to the
A object that A.f() returns, then the above code should let you
do that properly...

-Peter
 
S

Stuart McGraw

Sorry, you are right, I wasn't clear. I mean B inherits from
A. Here is what I am trying to do...

Class A has a method A.a() that returns an A. I want a
identical class but with an additional property .newprop
and method .b() And I want .a() to return a B, not an A.

class B (A):
def __init__(self, *args, **kwds):
A.__init__(self, *args, **kwds)
self.newprop = 99
def a(self):
x = A.a(self) # x is an A
x.__class__ = B
return x # I want x to be a B, i.e have b() and .newprop.
def b(self):
...something...

Yes, I know this is bogus. But I am not sure what
I should be doing. And to correct what I originally
posted, A is implented in python (but I still can't
change it for administrative reasons), but it's properties
are declared with "__slots__ = [...]" if that makes a
difference. This is all in Python 2.3.3.
 
A

Aahz

Class A has a method A.a() that returns an A. I want a
identical class but with an additional property .newprop
and method .b() And I want .a() to return a B, not an A.

class B (A):
def __init__(self, *args, **kwds):
A.__init__(self, *args, **kwds)
self.newprop = 99
def a(self):
x = A.a(self) # x is an A
x.__class__ = B
return x # I want x to be a B, i.e have b() and .newprop.
def b(self):
...something...

Yes, I know this is bogus. But I am not sure what I should be doing.
And to correct what I originally posted, A is implented in python
(but I still can't change it for administrative reasons), but it's
properties are declared with "__slots__ = [...]" if that makes a
difference. This is all in Python 2.3.3.

class A:
def a(self):
return self.__class__()
 
S

Scott David Daniels

Stuart said:
Sorry, you are right, I wasn't clear. I mean B inherits from
A. Here is what I am trying to do...

Class A has a method A.a() that returns an A. I want a
identical class but with an additional property .newprop
and method .b() And I want .a() to return a B, not an A.

class B (A):
def __init__(self, *args, **kwds):
A.__init__(self, *args, **kwds)
self.newprop = 99
def a(self):
x = A.a(self) # x is an A
x.__class__ = B
return x # I want x to be a B, i.e have b() and .newprop.
def b(self):
...something...

Yes, I know this is bogus. But I am not sure what
I should be doing.
Typically, you might want to do something like:

class B(A):
...
def a(self):
x = self.__class__.__new__(self.__class__,...)
# __new__ Usually gets no more args here, but

x.__init__(...)
# And here is where we do the actual init

...

Hope this helps

-Scott David Daniels
(e-mail address removed)
 
P

Paul McGuire

class A:
def a(self):
return self.__class__()
--
OP can't do this, can't get at source code of A.

Assuming that you don't want to write __init__() to take an A argument, you
need something like:

class B(A):
def makeFromAnA(other): # other is an A instance
# construct a B - could pass initialization args from
# other if this is part of the interface
newB = B()

# ... copy base class A fields from other to newB here ...

# ... add B-ish stuff to newB here ...

return newB
makeFromAnA=staticmethod(makeFromAnA)

def a(self):
return B.makeFromAnA( A.a() )

Here's an example:
import random
class Point2D(object): # pretend this is implemented in C, so we can't
change the code
def __init__(self,xval,yval):
self.x = xval
self.y = yval
def randomPoint():
return Point2D( random.random()*100, random.random()*100 )
randomPoint=staticmethod(randomPoint)

class Point3D(Point2D): # not really good O-O design, but that's not the,
um, point
def __init__(self,xval,yval,zval):
self.x = xval
self.y = yval
self.z = zval
def make3DPtFrom2DPt(other):
print "Make 3D pt from",other
return Point3D(other.x,other.y,0)
# or if the __init__'s are not similar,
# manually assign fields
newPt3D = Point3D(0,0,0)
newPt3D.x = other.x
newPt3D.y = other.y
return newPt3D
make3DPtFrom2DPt=staticmethod(make3DPtFrom2DPt)
def randomPoint():
newPt = Point3D.make3DPtFrom2DPt( Point2D.randomPoint() )
newPt.z = random.random()*100
return newPt
randomPoint=staticmethod(randomPoint)

print Point2D.randomPoint()
print Point3D.randomPoint()

-- Paul
 
J

John Roth

Stuart McGraw said:
Sorry, you are right, I wasn't clear. I mean B inherits from
A. Here is what I am trying to do...

Class A has a method A.a() that returns an A. I want a
identical class but with an additional property .newprop
and method .b() And I want .a() to return a B, not an A.

class B (A):
def __init__(self, *args, **kwds):
A.__init__(self, *args, **kwds)
self.newprop = 99
def a(self):
x = A.a(self) # x is an A
x.__class__ = B
return x # I want x to be a B, i.e have b() and .newprop.
def b(self):
...something...

Yes, I know this is bogus. But I am not sure what
I should be doing. And to correct what I originally
posted, A is implented in python (but I still can't
change it for administrative reasons), but it's properties
are declared with "__slots__ = [...]" if that makes a
difference. This is all in Python 2.3.3.

What's bogus about it? You can change the
class of an instance to be anything you want.
The only issue might be the slots; I'm not all
that familiar with what restrictions they might
impose.

Granted, there are relatively few cases
where changing the class of an instance on
the fly is actually better than the alternatives,
but this might be one of them.

John Roth
 
P

Paul Rubin

Stuart McGraw said:
Class A has a method A.a() that returns an A. I want a
identical class but with an additional property .newprop
and method .b() And I want .a() to return a B, not an A.

class B (A):
def __init__(self, *args, **kwds):
A.__init__(self, *args, **kwds)
self.newprop = 99
def a(self):
x = A.a(self) # x is an A
x.__class__ = B
return x # I want x to be a B, i.e have b() and .newprop.
Ugh!!

Yes, I know this is bogus. But I am not sure what I should be doing.

I think you have to make B into a container for an A. Something like:

class B(A):
def __init__(self, *args, **kwds):
self.newprop = 99
self.wrapped_A = A(*args, **kwds)
def a(self):
x = B()
x.wrapped_A = A.a(self.wrapped_A)
return # I want x to be a B, i.e have b() and .newprop.
def __getattr__(self, attr):
# delegate all inherited operations to the wrapped A object
return A.__getattr__(self.wrapped_A, attr)

You might also be able to do something crazy, like change A's metaclass
so that its __new__ operation can make a B under certain circumstances.
 
S

Stuart McGraw

Scott David Daniels said:
Typically, you might want to do something like:

class B(A):
...
def a(self):
x = self.__class__.__new__(self.__class__,...)
# __new__ Usually gets no more args here, but

x.__init__(...)
# And here is where we do the actual init

I don't think this will work. A.a() returns an A, but one that
is initialized differently than an A() instance. That is, A.a()
does more that just __new__() and __init__() to it. So if I
do the above, I end up with a subtype (right word?) of an
A(), not an A.a().

Now I think that my B.a() must call A.a() and somehow
dynamically change the type of the object received? As suggesed
above, I tried to change the class (x.__class__ = B) but that
just results in an exception
TypeError: __class__ assignment: 'B' object layout differs from 'A'

Or maybe what I am trying to do is not possible in Python?
 
S

Stuart McGraw

Paul Rubin said:
I think you have to make B into a container for an A. Something like:

class B(A):
def __init__(self, *args, **kwds):
self.newprop = 99
self.wrapped_A = A(*args, **kwds)
def a(self):
x = B()
x.wrapped_A = A.a(self.wrapped_A)
return # I want x to be a B, i.e have b() and .newprop.
def __getattr__(self, attr):
# delegate all inherited operations to the wrapped A object
return A.__getattr__(self.wrapped_A, attr)

You might also be able to do something crazy, like change A's metaclass
so that its __new__ operation can make a B under certain circumstances.

Ahhhh.... I did not know about delegation. I do now, thanks.
A minor disadvantage is that, because I also delegate __setattr__
all the instance variable have to be assigned in the form
self.__dict__['var'], rather than self.var (as I discovered the hard
way). Ugly, but I can live with it. Thanks again.
 
S

Scott David Daniels

Stuart said:
Paul Rubin said:
>>...
class B(A):
def __init__(self, *args, **kwds):
self.newprop = 99
self.wrapped_A = A(*args, **kwds)
def a(self):
x = B()
x.wrapped_A = A.a(self.wrapped_A)
return # I want x to be a B, i.e have b() and .newprop.
def __getattr__(self, attr):
# delegate all inherited operations to the wrapped A object
return A.__getattr__(self.wrapped_A, attr)

You might also be able to do something crazy, like change A's metaclass
so that its __new__ operation can make a B under certain circumstances.

Ahhhh.... I did not know about delegation. I do now, thanks.
A minor disadvantage is that, because I also delegate __setattr__
all the instance variable have to be assigned in the form
self.__dict__['var'], rather than self.var (as I discovered the hard
way). Ugly, but I can live with it. Thanks again.

If you can separate the names, you can do something like:

class B(object):
def __init__(self, *args, **kwargs):
self._a = A(*args, **kwargs)
self.newprop = 99
def __setattr__(self, name, val):
if name.startswith('_') or name == 'newprop':
self.__dict__[name] = val
else:
setattr(self._a, name, val)
def getattr(self, name):
return getattr(self._a, name)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top