Mix-In Class Methods At Run-Time

D

digitalorganics

Hi all. If I have an instance of class A, called say foo, and I need to
mix-in the functions and variables of another class (class B) to this
instance at runtime, how do I do it? In other words, I want to make foo
an instance of an anonymous and temporary class that inherits its
functionality from classes A and B, while at the same time I want the
pre-existing contents of foo to remain intact. Possible in Python?

Thanks....
 
B

bearophileHUGS

I think it's possible, most of such kind of things are possible with
Python.
I'm not an expert yet in such kind of things, so this can be a starting
point for you (note the shadowing of m2, the class docstrings, etc).
Other people can give you something better or more correct.

class A:
def m1(self): return "m1"
def m2(self): return "m2"

class B:
def m3(self): return "m3"

class P:
def m2(self): return "m2b"
def m4(self): return"m4"

def mixin(object, *classes):
class NewClass(object.__class__):
pass
for C in classes:
NewClass.__dict__.update(C.__dict__)
object.__class__ = NewClass

foo = P()
print "Before:"
print "foo.__class__.__dict__.keys():", foo.__class__.__dict__.keys()
print "P.__dict__.keys():", P.__dict__.keys()
print "foo.m2():", foo.m2()
print "foo.m4():", foo.m4(), "\n"

mixin(foo, A, B)

print "After:"
print "foo.__class__.__dict__.keys():", foo.__class__.__dict__.keys()
print "P.__dict__.keys():", P.__dict__.keys()
print "foo.m1():", foo.m1()
print "foo.m2():", foo.m2()
print "foo.m3():", foo.m3()
print "foo.m4():", foo.m4()

Bye,
bearophile
 
D

digitalorganics

This looks excellent bearophile, but I'm having trouble understanding
some things. Perhaps you can help wipe clean my ignorance. Firstly, I
thought __classes__ was a read-only attribute? Secondly, what is a
"dictproxy object" and why won't the following code work:

class Cat:
def meow(self):
print "meow"
def MixIn(object, *classes):
temp = type('ClassPie', (object.__class__,) + classes, {})
temp.__dict__.update([object.__dict__])
NewClass = MixIn(Cat(), C, D)
test = NewClass()

__dict__, to my understanding, is suppose to be a dictionary, but
Python keeps telling me it's a 'dictproxy' object and that it has no
attribute 'update'. Why is this?

Thanks.
 
D

digitalorganics

Okay, while I'd still like to know the answer(s) to my earlier
question(s), I've mostly solved my problem thanks to bearophile and my
own learning. An example:

class Cat(object):
def __init__(self):
self.love = 0
def meow(self):
print "meow"
class Dog(object):
def bark(self):
print "bark"
class Bat(object):
def scream(self):
print "scream"
def Mixin(object, *classes):
NewClass = type('Mixin', (object.__class__,) + classes, {})
newobj = NewClass()
newobj.__dict__.update(object.__dict__)
return newobj
mycat = Cat()
mycat.love = 4
mycat = Mixin(mycat, Dog, Bat)
print mycat.__dict__, mycat.__class__, mycat.__class__.__bases__
print dir(mycat)

Two additional questions though: 1) is there a way for a function to
get a reference to its caller automatically (as in, without the caller
having to pass it in)? and 2) what's the reason to use newstyle classes
versus the old? In order to create the dynamic class "NewClass" in the
code above I called type() but that requires at least one new style
class as a base. Thus, I had to have at least one of my animals inherit
from "object" and this seemed a nuisance since I don't at this point
know what the benefit of "newstyle" classes is. I was going to just use
the new module and classobj() but I read somewhere that that was
unadvisable and newstyle classes should be used in new code. ...

Thank you.

Cheers,
DigiO

This looks excellent bearophile, but I'm having trouble understanding
some things. Perhaps you can help wipe clean my ignorance. Firstly, I
thought __classes__ was a read-only attribute? Secondly, what is a
"dictproxy object" and why won't the following code work:

class Cat:
def meow(self):
print "meow"
def MixIn(object, *classes):
temp = type('ClassPie', (object.__class__,) + classes, {})
temp.__dict__.update([object.__dict__])
NewClass = MixIn(Cat(), C, D)
test = NewClass()

__dict__, to my understanding, is suppose to be a dictionary, but
Python keeps telling me it's a 'dictproxy' object and that it has no
attribute 'update'. Why is this?

Thanks.


I think it's possible, most of such kind of things are possible with
Python.
I'm not an expert yet in such kind of things, so this can be a starting
point for you (note the shadowing of m2, the class docstrings, etc).
Other people can give you something better or more correct.

class A:
def m1(self): return "m1"
def m2(self): return "m2"

class B:
def m3(self): return "m3"

class P:
def m2(self): return "m2b"
def m4(self): return"m4"

def mixin(object, *classes):
class NewClass(object.__class__):
pass
for C in classes:
NewClass.__dict__.update(C.__dict__)
object.__class__ = NewClass

foo = P()
print "Before:"
print "foo.__class__.__dict__.keys():", foo.__class__.__dict__.keys()
print "P.__dict__.keys():", P.__dict__.keys()
print "foo.m2():", foo.m2()
print "foo.m4():", foo.m4(), "\n"

mixin(foo, A, B)

print "After:"
print "foo.__class__.__dict__.keys():", foo.__class__.__dict__.keys()
print "P.__dict__.keys():", P.__dict__.keys()
print "foo.m1():", foo.m1()
print "foo.m2():", foo.m2()
print "foo.m3():", foo.m3()
print "foo.m4():", foo.m4()

Bye,
bearophile
 
B

Bruno Desthuilliers

Two additional questions though: 1) is there a way for a function to
get a reference to its caller automatically (as in, without the caller
having to pass it in)?

It's possible with sys._getframe() and a decorator - but consider it a
hack.
and 2) what's the reason to use newstyle classes
versus the old?

All this is explained on python.org (there's a menu entry for this in
the documentation menu). AFAICT, newstyle classes can do whatever
oldstyle classes did, *and much more* (descriptors and usable
metaclasses) - and they are somewhat faster too. So - compatibility with
older Python versions (< 2.2 IIRC) set aside -, there's just no reason
to use oldstyle classes.
In order to create the dynamic class "NewClass" in the
code above I called type() but that requires at least one new style
class as a base. Thus, I had to have at least one of my animals inherit
from "object" and this seemed a nuisance since

OMG, eight more keystrokes - talk about a nuisance...
I don't at this point
know what the benefit of "newstyle" classes is.

See it the other way round : the *only* benefit of oldstyle classes is
compatibility with pre-2.2 Python versions.
 
B

bearophileHUGS

I can't give much answers, I am not that expert yet.

Bruno Desthuilliers:
newstyle classes can do whatever oldstyle classes
did, *and much more* (descriptors and usable
metaclasses) - and they are somewhat faster too.

In the past I have done few tests, and it seemed that new style classes
are a bit slower (but the difference doesn't make much difference).
Maybe for Py2.5 the situation will be different.

Bye,
bearophile
 
D

digitalorganics

Bruno said:
It's possible with sys._getframe() and a decorator - but consider it a
hack.

Gotcha, thanks.
All this is explained on python.org (there's a menu entry for this in
the documentation menu). AFAICT, newstyle classes can do whatever
oldstyle classes did, *and much more* (descriptors and usable
metaclasses) - and they are somewhat faster too. So - compatibility with
older Python versions (< 2.2 IIRC) set aside -, there's just no reason
to use oldstyle classes.


OMG, eight more keystrokes - talk about a nuisance...

Like, Oh My God! *claps hand to mouth* lol You humor me. Yes, eight
more keystrokes. I follow the general rule of, if I'm going to put in
extra effort, I'd like to know why. So you see, it's not so much an
adversion to the eight keystrokes (multiplied by however many classes I
have mind you), but to not knowing why I should use them. If I don't
care for descriptors or metaclasses, I don't see why I should feel
compelled to use them. And when I decide I want/need these features, I
can put the eight keystroke in at that time. No biggy. :) Thanks
Bruno.
 
B

Bruno Desthuilliers

(snip)



Like, Oh My God! *claps hand to mouth* lol You humor me. Yes, eight
more keystrokes. I follow the general rule of, if I'm going to put in
extra effort,

OMG, eight more keystrokes - talk about extra effort !-)
I'd like to know why.

The only reason for *not* doing it would be compat issues with pre 2.2.x
versions.
So you see, it's not so much an
adversion to the eight keystrokes (multiplied by however many classes I
have mind you),

Strange enough, I do write my share of Python code, and don't even
notice typing the EightKeystrokes.
but to not knowing why I should use them.

Because they are kind of the standard Python object model since 2.2.x ?-)

Did you at least take time to read the doc on newstyle classes on
python.org ?

FWIW, with 2.5, even exceptions are now newstyle classes. No more
oldstyle classes in the builtins. And AFAICT, no more oldstyle classes
in the standard lib neither. Does that ring a bell ?
If I don't
care for descriptors or metaclasses,

You *do* care for descriptors. Without descriptors, no properties, no
classmethods, no staticmethods... What a desolation :(

More seriously, given what you're into actually, not caring about what
one can do with newstyle classes seems really strange to me - like
digging a swimming-pool with a pick and a shovel when you have an
excavator... (disclaimer : google translation, not sure it makes sens in
english...)
I don't see why I should feel
compelled to use them.

"them" -> "newstyle classes" or "descriptors and metaclasses" ?
And when I decide I want/need these features, I
can put the eight keystroke in at that time.

What can I say ? That's your code, not mine...
 
D

digitalorganics

Bruno said:
OMG, eight more keystrokes - talk about extra effort !-)

As I said, its not the effort, its the personal need to know why the
effort, however small, is being put in.
The only reason for *not* doing it would be compat issues with pre 2.2.x
versions.


Strange enough, I do write my share of Python code, and don't even
notice typing the EightKeystrokes.


Because they are kind of the standard Python object model since 2.2.x ?-)

What kind of answer is that? What does that actually mean to me? I'm
talking practical reasons here....
Did you at least take time to read the doc on newstyle classes on
python.org ?

FWIW, with 2.5, even exceptions are now newstyle classes. No more
oldstyle classes in the builtins. And AFAICT, no more oldstyle classes
in the standard lib neither. Does that ring a bell ?

Yes yes, but I've been able to use all that without declaring a single
new-style class. So my question was, what penalty do I actually pay...
You *do* care for descriptors. Without descriptors, no properties, no
classmethods, no staticmethods... What a desolation :(

More seriously, given what you're into actually, not caring about what
one can do with newstyle classes seems really strange to me - like
digging a swimming-pool with a pick and a shovel when you have an
excavator... (disclaimer : google translation, not sure it makes sens in
english...)

You have it backwards. Of course I care what can be done with new-style
classes. That was precisely the basis for my original question. I
wanted to know what those eight keystrokes * #ofclasses gets me, and if
its necessary versus just a key to unlock features that I may or may
not need.
 

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,756
Messages
2,569,540
Members
45,025
Latest member
KetoRushACVFitness

Latest Threads

Top