how to dynamically instantiate an object inheriting from severalclasses?

J

Joe Strout

I have a function that takes a reference to a class, and then
instantiates that class (and then does several other things with the
new instance). This is easy enough:

item = cls(self, **itemArgs)

where "cls" is the class reference, and itemArgs is obviously a set of
keyword arguments for its __init__ method.

But now I want to generalize this to handle a set of mix-in classes.
Normally you use mixins by creating a class that derives from two or
more other classes, and then instantiate that custom class. But in my
situation, I don't know ahead of time which mixins might be used and
in what combination. So I'd like to take a list of class references,
and instantiate an object that derives from all of them, dynamically.

Is this possible? If so, how?

Thanks,
- Joe
 
A

Arnaud Delobelle

Joe Strout said:
I have a function that takes a reference to a class, and then
instantiates that class (and then does several other things with the
new instance). This is easy enough:

item = cls(self, **itemArgs)

where "cls" is the class reference, and itemArgs is obviously a set of
keyword arguments for its __init__ method.

But now I want to generalize this to handle a set of mix-in classes.
Normally you use mixins by creating a class that derives from two or
more other classes, and then instantiate that custom class. But in my
situation, I don't know ahead of time which mixins might be used and
in what combination. So I'd like to take a list of class references,
and instantiate an object that derives from all of them, dynamically.

Is this possible? If so, how?

Of course it's possible: use type(name, bases, dict).
True

Call-by-object'ly yours
 
G

George Sakkis

I have a function that takes a reference to a class, and then  
instantiates that class (and then does several other things with the  
new instance).  This is easy enough:

    item = cls(self, **itemArgs)

where "cls" is the class reference, and itemArgs is obviously a set of  
keyword arguments for its __init__ method.

But now I want to generalize this to handle a set of mix-in classes.  
Normally you use mixins by creating a class that derives from two or  
more other classes, and then instantiate that custom class.  But in my  
situation, I don't know ahead of time which mixins might be used and  
in what combination.  So I'd like to take a list of class references,  
and instantiate an object that derives from all of them, dynamically.

Is this possible?  If so, how?

Easily:

derived_cls = type('Derived', (cls1, cls2, *rest_classes), {})
item = derived_cls(**itemArgs)

You will probably want to cache the generated classes so that at most
one class is created for each combination of mixins.

HTH,
George
 
J

Joe Strout

Of course it's possible: use type(name, bases, dict).

Thanks, I never knew about that form of type(). Neither does the
2.5.2 reference manual, whose only index entry for the type() function
is <http://www.python.org/doc/2.5.2/ref/objects.html#l2h-21>, and that
speaks only about the traditional use of type() to check the type of
an object.

help(type) does mention the form you show, though it doesn't explain
what the dict is for.

Where would I find documentation on this nifty function?

Thanks,
- Joe
 
N

Ned Deily

Thanks, I never knew about that form of type(). Neither does the
2.5.2 reference manual, whose only index entry for the type() function
is <http://www.python.org/doc/2.5.2/ref/objects.html#l2h-21>, and that
speaks only about the traditional use of type() to check the type of
an object.

help(type) does mention the form you show, though it doesn't explain
what the dict is for.

Where would I find documentation on this nifty function?

Where built-in functions are documented, the Python Library Reference:

<http://www.python.org/doc/2.5.2/lib/built-in-funcs.html>
 
S

Steve Holden

Joe said:
Perfect, thank you. (Odd that the index entry for type() doesn't link
to this page.)

Yeah, the indexing isn't perfect. Not sure what to do about that without
filing bugs for each case (which I am as guilty of not doing as anyone
else).

regards
Steve
 
S

Steven D'Aprano

I have a function that takes a reference to a class,

Hmmm... how do you do that from Python code? The simplest way I can think
of is to extract the name of the class, and then pass the name as a
reference to the class, and hope it hasn't been renamed in the meantime:

def foo(cls_name, item_args):
# Won't necessarily work for nested scopes.
cls = globals()[cls_name]
item = cls(**itemArgs)
return item

instance = foo(Myclass.__name__, {'a':1})

Seems awfully complicated. If I were you, I'd forget the extra layer of
indirection and just pass the class itself, rather than trying to
generate some sort of reference to it. Let the Python virtual machine
worry about what is the most efficient mechanism to use behind the scenes.


[...]
But now I want to generalize this to handle a set of mix-in classes.
Normally you use mixins by creating a class that derives from two or
more other classes, and then instantiate that custom class. But in my
situation, I don't know ahead of time which mixins might be used and in
what combination. So I'd like to take a list of class references, and
instantiate an object that derives from all of them, dynamically.

Is this possible? If so, how?

It sounds like you need to generate a new class on the fly. Here's one
way:

# untested
def foo(cls, item_args, mixins=None):
superclasses = [cls] + (mixins or [])
class MixedClass(*superclasses):
pass
item = MixedClass(**itemArgs)
return item

instance = foo(MyClass, {'a':1}, [Aclass, Bclass, Cclass])
 
J

Joe Strout

Hmmm... how do you do that from Python code? The simplest way I can
think
of is to extract the name of the class, and then pass the name as a
reference to the class, and hope it hasn't been renamed in the
meantime...

Please quit trying to confuse the kids at home. Classes in Python are
first-class objects, and any time you refer to a class or any other
object in Python, what you have is a reference to it.

<http://www.strout.net/info/coding/valref/>

Cheers,
- Joe
 
A

Arnaud Delobelle

Joe Strout said:
Please quit trying to confuse the kids at home. Classes in Python are
first-class objects, and any time you refer to a class or any other
object in Python, what you have is a reference to it.

<http://www.strout.net/info/coding/valref/>

Why don't you put this link in your sig? It'll save you time. If I
were you though I would wait a bit until I have had more experience with
Python (In case you change your mind).

In the meantime why not adopt the terminology commonly used on this list
and by Python users at large? Nobody else would talk about 'a function
that takes a reference to a class'. It's just plain confusing.
 
S

Steven D'Aprano

Please quit trying to confuse the kids at home. Classes in Python are
first-class objects, and any time you refer to a class or any other
object in Python, what you have is a reference to it.


No, at the level of Python code, what you have is the object itself.

What any specific implementation of the Python virtual machine almost
certainly will have are references, or pointers, or some other
implementation-specific form of indirection that eventually leads to the
object. But at the level of Python code, they are invisible. There are no
references at the level of Python code.

The burden of proof lies on you to prove that "reference to a class" has
any meaning at the level of Python code. First you need to define what a
reference is, in terms of *Python* entities (not implementation details),
how it differs from the object itself, and how you can tell "a reference
to a class" apart from the class itself, *at the level of Python code*.

I don't think you can do it, not without conflating implementation-level
details with Python-level details. In Python code, you have objects, and
that is all. Talking about "a reference to a class" instead of "a class"
is simply obscurantism.
 
G

George Sakkis

Please quit trying to confuse the kids at home.  Classes in Python are  
first-class objects, and any time you refer to a class or any other  
object in Python, what you have is a reference to it.

...which makes the phrase "a reference to an X" a more verbose,
redundant version of "an X" since it applies to *every* Python object.
You have made your point in the 300+ posts thread, so please quit the
terminology trolling in every new thread.

George
 
R

Rafe

I have a function that takes a reference to a class,

Hmmm... how do you do that from Python code? The simplest way I can think
of is to extract the name of the class, and then pass the name as a
reference to the class, and hope it hasn't been renamed in the meantime:

def foo(cls_name, item_args):
    # Won't necessarily work for nested scopes.
    cls = globals()[cls_name]
    item = cls(**itemArgs)
    return item

instance = foo(Myclass.__name__, {'a':1})

Seems awfully complicated. If I were you, I'd forget the extra layer of
indirection and just pass the class itself, rather than trying to
generate some sort of reference to it. Let the Python virtual machine
worry about what is the most efficient mechanism to use behind the scenes..

[...]
But now I want to generalize this to handle a set of mix-in classes.
Normally you use mixins by creating a class that derives from two or
more other classes, and then instantiate that custom class.  But in my
situation, I don't know ahead of time which mixins might be used and in
what combination.  So I'd like to take a list of class references, and
instantiate an object that derives from all of them, dynamically.
Is this possible?  If so, how?

It sounds like you need to generate a new class on the fly. Here's one
way:

# untested
def foo(cls, item_args, mixins=None):
    superclasses = [cls] + (mixins or [])
    class MixedClass(*superclasses):
        pass
    item = MixedClass(**itemArgs)
    return item

instance = foo(MyClass, {'a':1}, [Aclass, Bclass, Cclass])

I find type() is the better way to go because it allows you to name
the resulting class as well. It may make debugging a little easier.
Using a hard-coded class, such as "MixedClass" in the above example,
with dynamic bases produces lots of "MixedClass" instances with
different interfaces/abilities.

- Rafe
 
M

Matimus

I have a function that takes a reference to a class, and then  
instantiates that class (and then does several other things with the  
new instance).  This is easy enough:

    item = cls(self, **itemArgs)

where "cls" is the class reference, and itemArgs is obviously a set of  
keyword arguments for its __init__ method.

But now I want to generalize this to handle a set of mix-in classes.  
Normally you use mixins by creating a class that derives from two or  
more other classes, and then instantiate that custom class.  But in my  
situation, I don't know ahead of time which mixins might be used and  
in what combination.  So I'd like to take a list of class references,  
and instantiate an object that derives from all of them, dynamically.

Is this possible?  If so, how?

Thanks,
- Joe

I wrote this a while ago. I sort of regret it though. Mixins could
(and I will argue should) be avoided most of the time by delegating to
other objects with less functionality. Utilizing many mixin classes
tends to just make gigantic classes. This is a huge violation of the
"Single Responsibility Principle". That isn't to say that I don't
think there is a place for multiple inheritance. Multiple inheritance
to the point where it is easier to write a metaclass to automatically
generate your __init__ method than it is to write it yourself is a
good indicator that you have gone too far. Which is what I did.

code:

import inspect

class AutoGenInitMetaError(Exception):
""" Exception is raised if AutoGenInitMeta cannot auto-generate a
constructor for a class because of conflicting parameters in base
classes.
"""
pass

class AutoGenInitMeta(type):
""" Meta-Class for automatically generating __init__ method for a
class
with multiple mixin base classes.
"""
def __new__(cls, name, bases, assoc):
if "__init__" in assoc:
return super(AutoGenInitMeta, cls).__new__(cls, name,
bases, assoc)
args = ['self']
dargs = []
defaults = []
tmpl = []
varg = None
vkwarg = None
tmpl = ["def __init__%s:\n"]
for base in bases[::-1]:
a, va, vkw, d = argspec = inspect.getargspec
(base.__init__)
argspecfmt = inspect.formatargspec(*argspec[:3])
if d:
num_d = len(d)
args += a[1:-num_d]
defaults += d
dargs += a[-num_d:]
else:
# remember to stip off self
args += a[1:]
if va is not None:
if varg is not None:
raise AutoGenInitMetaError(
"There must be only one `*` arg in base
constructors"
)
varg = va

if vkw is not None:
if vkwarg is not None:
raise AutoGenInitMetaError(
"There must be only one `**` arg in base
constructors"
)
vkwarg = vkw
tmpl.append(" %s.__init__%s\n"%(base.__name__,
argspecfmt))
tmpl = "".join(tmpl)
argspec = (args + dargs, varg, vkwarg, defaults)
exec tmpl%inspect.formatargspec(*argspec) in globals(), assoc

return super(AutoGenInitMeta, cls).__new__(cls, name, bases,
assoc)


How do you use it?
.... def __init__(self, a, b):
.... self.a = a
.... self.b = b
........ def __init__(self, c, d):
.... self.c = c
.... self.d = d
........ __metaclass__ = AutoGenInitMeta
....
Notice that the arguments to D came before C. So you have to list the
classes in reverse order of how you want the arguments listed.

I post it as an example of a "neat python trick". Even the "neat"
might be self indulgence. I encourage anybody tempted to use this to
refactor/redesign instead.

Matt
 
J

Joe Strout

I wrote this a while ago. I sort of regret it though. Mixins could
(and I will argue should) be avoided most of the time by delegating to
other objects with less functionality. Utilizing many mixin classes
tends to just make gigantic classes. This is a huge violation of the
"Single Responsibility Principle".

I see your point, but in this case, I'm building a layout/config tool
for use with wxPython, and wx uses a fair number of mixins (especially
for the list control).
That isn't to say that I don't
think there is a place for multiple inheritance. Multiple inheritance
to the point where it is easier to write a metaclass to automatically
generate your __init__ method than it is to write it yourself is a
good indicator that you have gone too far. Which is what I did.

I appreciate the sample code, and the thoughtful comments. You do
yourself credit.

Best,
- Joe
 

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,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top