Dynamic subclassing ?

M

manatlan

I've got an instance of a class, ex :

b=gtk.Button()

I'd like to add methods and attributes to my instance "b".
I know it's possible by hacking "b" with setattr() methods. But i'd
like to do it with inheritance, a kind of "dynamic subclassing",
without subclassing the class, only this instance "b" !

In fact, i've got my instance "b", and another class "MoreMethods"

class MoreMethods:
def sayHello(self):
print "hello"

How could i write ...

"b = b + MoreMethods"

so "b" will continue to be a gtk.Button, + methods/attributs of
MoreMethods (it's what i call "dynamic inheritance") ...so, things
like this should work :

- b.set_label("k")
- b.sayHello()

I can't find the trick, but i'm pretty sure it's possible in an easy
way.
Help is welcome
thanx
 
M

manatlan

manatlan a écrit :


You don't even need setattr() here, you can set the attributes directly.





You don't necessarily need subclassing here. What you want is a typical
use case of the Decorator pattern:

class MoreMethods(object):
def __init__(self, button):
self._button = button

def sayHello(self):
print "hello"

def __getattr__(self, name):
return getattr(self._button, name)

def __setattr__(self, name, value):
if name in dir(self._button):
setattr(self._button, name, value)
else:
object.__setattr__(self, name, value)

b = MoreMethods(gtk.Button())
b.set_label("k")
b.say_hello()

except that "b" is not anymore a "gtk.Button", but a "MoreMethods"
instance ...
i'd like that "b" stay a "gtk.Button" ...
 
B

Bruno Desthuilliers

manatlan a écrit :
I've got an instance of a class, ex :

b=gtk.Button()

I'd like to add methods and attributes to my instance "b".
I know it's possible by hacking "b" with setattr() methods.

You don't even need setattr() here, you can set the attributes directly.

But i'd
like to do it with inheritance, a kind of "dynamic subclassing",
without subclassing the class, only this instance "b" !

In fact, i've got my instance "b", and another class "MoreMethods"

class MoreMethods:
def sayHello(self):
print "hello"

How could i write ...

"b = b + MoreMethods"

so "b" will continue to be a gtk.Button, + methods/attributs of
MoreMethods (it's what i call "dynamic inheritance") ...so, things
like this should work :

- b.set_label("k")
- b.sayHello()

I can't find the trick, but i'm pretty sure it's possible in an easy
way.

You don't necessarily need subclassing here. What you want is a typical
use case of the Decorator pattern:

class MoreMethods(object):
def __init__(self, button):
self._button = button

def sayHello(self):
print "hello"

def __getattr__(self, name):
return getattr(self._button, name)

def __setattr__(self, name, value):
if name in dir(self._button):
setattr(self._button, name, value)
else:
object.__setattr__(self, name, value)

b = MoreMethods(gtk.Button())
b.set_label("k")
b.say_hello()
 
D

Daniel Nogradi

I've got an instance of a class, ex :
b=gtk.Button()

I'd like to add methods and attributes to my instance "b".
I know it's possible by hacking "b" with setattr() methods. But i'd
like to do it with inheritance, a kind of "dynamic subclassing",
without subclassing the class, only this instance "b" !

In fact, i've got my instance "b", and another class "MoreMethods"

class MoreMethods:
def sayHello(self):
print "hello"

How could i write ...

"b = b + MoreMethods"

so "b" will continue to be a gtk.Button, + methods/attributs of
MoreMethods (it's what i call "dynamic inheritance") ...so, things
like this should work :

- b.set_label("k")
- b.sayHello()

I can't find the trick, but i'm pretty sure it's possible in an easy
way.

How about:

class MoreMethods:
def sayHello(self):
print "hello"

class myButton( gtk.Button, MoreMethods ):
pass

b = myButton( )

isinstance( b, gtk.Button ) # True
b.sayHello( ) # "hello"


Daniel
 
M

manatlan

How about:

class MoreMethods:
def sayHello(self):
print "hello"

class myButton( gtk.Button, MoreMethods ):
pass

b = myButton( )

isinstance( b, gtk.Button ) # True
b.sayHello( ) # "hello"

yes, but it needs to recreate an instance (of mybutton) ...
i can't do that, in my context.
The only things i've got is my instance "b" (which can be whatever
object).
i'd like to add methods/attributes to this instance, by adding a
heritage of another class.
thanks
 
K

Karlo Lozovina

manatlan said:
I can't find the trick, but i'm pretty sure it's possible in an easy
way.

It's somewhat easy, boot looks ugly to me. Maybe someone has a more
elegant solution:

In [6]: import new

In [13]: class Button:
....: def buttonFunc(self):
....: pass

In [14]: class ExtensionClass:
....: def extendedMethod(self):
....: pass

In [15]: hybrid = new.instance(Button,
Button.__dict__.update(ExtensionClass.__dict__))

In [17]: dir(hybrid)
Out[17]: ['__doc__', '__module__', 'buttonFunc', 'extendedMethod']

Seems to do what you want it to do.

HTH,
Karlo.
 
D

Daniel Nogradi

I've got an instance of a class, ex :
yes, but it needs to recreate an instance (of mybutton) ...
i can't do that, in my context.
The only things i've got is my instance "b" (which can be whatever
object).
i'd like to add methods/attributes to this instance, by adding a
heritage of another class.

I think that is not possible, at least in a simple way (there might be
a complicated way of messing with the MRO). Please anyone correct me
if I was wrong.

Daniel
 
S

Steven Bethard

manatlan said:
I've got an instance of a class, ex :

b=gtk.Button()

I'd like to add methods and attributes to my instance "b".
I know it's possible by hacking "b" with setattr() methods. But i'd
like to do it with inheritance, a kind of "dynamic subclassing",
without subclassing the class, only this instance "b" !

In fact, i've got my instance "b", and another class "MoreMethods"

class MoreMethods:
def sayHello(self):
print "hello"

How could i write ...

"b = b + MoreMethods"

You can simply bind the methods you want to add to the Button instance.
That means doing the equivalent of ``b.sayHello = sayHello.__get__(b)``.
For example::
... def set_label(self, label):
... print 'setting label:', label
... ... for name, value in cls.__dict__.items():
... if callable(value) and hasattr(value, '__get__'):
... setattr(obj, name, value.__get__(obj, type(obj)))
... Traceback (most recent call last):
... def say_hello(self):
... print 'hello'
... hello

HTH,

STeVe
 
A

Alex Martelli

manatlan said:
I've got an instance of a class, ex :

b=gtk.Button()

I'd like to add methods and attributes to my instance "b".
I know it's possible by hacking "b" with setattr() methods. But i'd
like to do it with inheritance, a kind of "dynamic subclassing",
without subclassing the class, only this instance "b" !

In fact, i've got my instance "b", and another class "MoreMethods"

class MoreMethods:
def sayHello(self):
print "hello"

How could i write ...

"b = b + MoreMethods"

so "b" will continue to be a gtk.Button, + methods/attributs of
MoreMethods (it's what i call "dynamic inheritance") ...so, things
like this should work :

- b.set_label("k")
- b.sayHello()

I can't find the trick, but i'm pretty sure it's possible in an easy
way.

I think what you're asking for is totally weird, and with just about
zero advantages compared with several saner alternatives that have
already been proposed in this thread and that you have rejects, but
sure, it's possible:

def addaclass(aninst, onemoreclass):
aninst.__class__ = type(aninst.__aclass__.__name__,
(aninst.__aclass__, onemoreclass), {})


Alex
 
M

manatlan

I think what you're asking for is totally weird, and with just about
zero advantages compared with several saner alternatives that have
already been proposed in this thread and that you have rejects, but
sure, it's possible:

def addaclass(aninst, onemoreclass):
aninst.__class__ = type(aninst.__aclass__.__name__,
(aninst.__aclass__, onemoreclass), {})

Alex

I know it's weird ... and solutions given here are a lot saner. But i
can't do that at the creation of the instance, because it's not me who
has the creation process ... the only things i've got, it's the
instance

i tried :

class MoreMethods:
def sayHello(self):
print "hello"

def addaclass(aninst, onemoreclass):
aninst.__class__ = type(aninst.__class__.__name__,
(aninst.__class__, onemoreclass), {})

b=gtk.Button("the_label")
addaclass(b,MoreMethods)
print b.get_label()
print b.hello()

but got :
"TypeError: __class__ assignment: only for heap types"
on the last line ...

I begin to think that the only solution is to use the module new like
Karlo said ...
 
M

manatlan

manatlan said:
I can't find the trick, but i'm pretty sure it's possible in an easy
way.

It's somewhat easy, boot looks ugly to me. Maybe someone has a more
elegant solution:

In [6]: import new

In [13]: class Button:
....: def buttonFunc(self):
....: pass

In [14]: class ExtensionClass:
....: def extendedMethod(self):
....: pass

In [15]: hybrid = new.instance(Button,
Button.__dict__.update(ExtensionClass.__dict__))

In [17]: dir(hybrid)
Out[17]: ['__doc__', '__module__', 'buttonFunc', 'extendedMethod']

Seems to do what you want it to do.

HTH,
Karlo.

yes, i think it's the only solution ... it's weird
but it seems to be the only one ...
i will try that ...
 
M

manatlan

You can simply bind the methods you want to add to the Button instance.
That means doing the equivalent of ``b.sayHello = sayHello.__get__(b)``.
For example::

... def set_label(self, label):
... print 'setting label:', label
...
... for name, value in cls.__dict__.items():
... if callable(value) and hasattr(value, '__get__'):
... setattr(obj, name, value.__get__(obj, type(obj)))
...
Traceback (most recent call last):

... def say_hello(self):
... print 'hello'
...
hello

HTH,

STeVe

Yes ! it seems very good ...
I will try that too
thanks a lot ...

class MoreMethods:
val=12
def hello(self):
self.val=13
return self.get_label()

def add_methods(obj, cls):
for name, value in cls.__dict__.items():
if callable(value) and hasattr(value, '__get__'):
setattr(obj, name, value.__get__(obj, type(obj)))
else:
setattr(obj, name, value)

b=gtk.Button("the_label")
add_methods(b,MoreMethods)
print b.get_label()
print b.val
print b.hello()
print b.val
 
M

manatlan

It's somewhat easy, boot looks ugly to me. Maybe someone has a more
elegant solution:
In [6]: import new
In [13]: class Button:
....: def buttonFunc(self):
....: pass
In [14]: class ExtensionClass:
....: def extendedMethod(self):
....: pass
In [15]: hybrid = new.instance(Button,
Button.__dict__.update(ExtensionClass.__dict__))
In [17]: dir(hybrid)
Out[17]: ['__doc__', '__module__', 'buttonFunc', 'extendedMethod']
Seems to do what you want it to do.
HTH,
Karlo.

yes, i think it's the only solution ... it's weird
but it seems to be the only one ...
i will try that ...

in fact, the "new" solution adds methods to all "gtk.Button" and not a
specific instance ... it's not good for me
but thanks (i always forget the use of the "new" module for this kind
of things)
 
B

Bruno Desthuilliers

manatlan a écrit :
except that "b" is not anymore a "gtk.Button", but a "MoreMethods"
instance ...
i'd like that "b" stay a "gtk.Button" ...

I don't understand why, but... Then write a function that "inject" all
the additionnal methods to your gtk.Button. Or do the
composition/delegation the other way round, and monkeypatch gtk.Button's
__getattr__.
 
A

Alex Martelli

manatlan said:
...
b=gtk.Button("the_label")
addaclass(b,MoreMethods) ...
"TypeError: __class__ assignment: only for heap types"
on the last line ...

Ah, yes, a type can be coded in such a way that you simply can't
reassign the __class__ of its instances, and apparently gtk.Button is
coded that way.

If you're keen on __class__ reassigning, you'll have to wrap such
classes before instance creation time, e.g.:

class weirdGtkButton(gtk.Button): pass

and use

b = weirdGtkButton("the label")

to generate the instances. That, of course, assumes that gtk.Button
isn't ALSO coded in ways that impede subclassing (as for all I know it
might be -- I don't have GTK around to check), in which case instead of
subclassing it you need a trickier wrap-and-hold approach (and you can
never pass isinstance checks, which subclassing would let you pass).


Alex
 
J

James T. Dennis

Karlo Lozovina said:
manatlan wrote:
It's somewhat easy, boot looks ugly to me. Maybe someone has a more
elegant solution:
In [6]: import new
In [13]: class Button:
....: def buttonFunc(self):
....: pass
In [14]: class ExtensionClass:
....: def extendedMethod(self):
....: pass
In [15]: hybrid = new.instance(Button,
Button.__dict__.update(ExtensionClass.__dict__))
In [17]: dir(hybrid)
Out[17]: ['__doc__', '__module__', 'buttonFunc', 'extendedMethod']
Seems to do what you want it to do.
HTH,
Karlo.

When I try something like this I run into a little problem:

class Foo:
def foo(self):
return "foo"
class Bar:
def bar(self):
return "bar"

f = Foo()
f.__dict__.update(Bar.__dict__)

... the problem is that while f now has a "bar" method it doesn't
work quite like a normal instance method:

>>> f.bar()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: bar() takes exactly 1 argument (0 given)
>>>

... though I can get by with f.bar(f)

This "new" module seems to be the key to it all; but the only
docs I have for that say:
Help on module new:

NAME
new - Create new objects of various types. Deprecated.

FILE
/usr/lib/python2.4/new.py

MODULE DOCS
http://www.python.org/doc/current/lib/module-new.html

DESCRIPTION
This module is no longer required except for backward compatibility.
Objects of most types can now be created by calling the type object.


... which sounds like a bad idea (from the word "Deprecated").
 
G

Gabriel Genellina

When I try something like this I run into a little problem:

class Foo:
def foo(self):
return "foo"
class Bar:
def bar(self):
return "bar"

f = Foo()
f.__dict__.update(Bar.__dict__)

... the problem is that while f now has a "bar" method it doesn't
work quite like a normal instance method:

>>> f.bar()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: bar() takes exactly 1 argument (0 given)
>>>

... though I can get by with f.bar(f)

Bar.__dict__ contains *unbound* methods - that is, methods not linked to
any particular instance. If you copy them directly into f.__dict__ you
lose the "magic" that binds methods to instances.
You could store a bound method into the instance but it's not a good idea
(there are cyclic references).
I think the easiest way is to define a dynamic class (as the subject on
this old thread suggests):

py> f = Foo()
py> Foo2 = type("Foo2", (Foo,Bar), {})
py> f.__class__ = Foo2
py> f.bar()
'bar'

The code above changes the object class once it was created, but you don't
have to. Also, you don't have to use a different class name (altough it
may be confusing...):

py> f = type("Foo", (Foo,Bar), {})()
py> f.bar()
'bar'
This "new" module seems to be the key to it all;

It's almost useless now that types are callable.
but the only docs I have for that say:
Help on module new:
[...]
MODULE DOCS
http://www.python.org/doc/current/lib/module-new.html

Did you follow the above link?
 

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

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top