selecting base class from user input

J

Jackson

I want a class that will determine its base class by the argument passed
in. What I am about to write _does_not_work_, but it shows what I am
trying to do.

class ABC(some_super):
def __init__(self,some_super):
some_super.__init__(self)

if some_super == list:
self.append('ABC')

elif some_super == dict:
self['ABC'] = None


Then, the user can call this function:
{'ABC': None}

Clearly, this is a bad example, but the central idea is what I am trying
to do. ABC is a particular example which can be represented in various
forms. I want an ABC class that will create the example in the form
specified by the user.

So how can I achieve this? Thanks.
 
D

danielx

Is your declaration of ABC supposed to have some_super as one of the
base classes? Your constructor has some_super as a parameter. What is
this supposed to mean in light of the declaration for ABC?

If you are trying to customize the base class of ABC by passing an
argument to the constructor of ABC, you should probably reconsider. If
constructing one instance of ABC can change ABC (the class) itself,
then the behavior of other instances will be affected as well. No
programmer can stay sane if he creates instances of a class that could
suddenly change its behavior (due to someone else's code).

What you could do instead is to create a function which constructs
classes based on the arguments it recieves. Then, you'll be able to
create instances of the generated classes (all this meta-thinking is
huring my brain ;). I am talking about something like this:

def createClass(name, base):
exec "class %s(%s): pass" % (name, base)
return eval( "name" )

....

Can you please tell us why you are doing this? My curiosity is killing
me!

Another meta-thought: Hopefully I've beaten everyone else to the punch
about that question. Is it just me, or will a reply with such a
question always tell the original poster that what he wants to do MUST
be flawed? I hope I have been gentler than this.
I want a class that will determine its base class by the argument passed
in. What I am about to write _does_not_work_, but it shows what I am
trying to do.

class ABC(some_super):
def __init__(self,some_super):
some_super.__init__(self)

if some_super == list:
self.append('ABC')

elif some_super == dict:
self['ABC'] = None


Then, the user can call this function:
example = ABC(list)
print example
['ABC']
example = ABC(dict)
print example
{'ABC': None}

Clearly, this is a bad example, but the central idea is what I am trying
to do. ABC is a particular example which can be represented in various
forms. I want an ABC class that will create the example in the form
specified by the user.

So how can I achieve this? Thanks.
 
J

Jackson

Thanks for the reply.

danielx wrote the following on 2006-08-13 19:49:
Is your declaration of ABC supposed to have some_super as one of the
base classes? Your constructor has some_super as a parameter. What is
this supposed to mean in light of the declaration for ABC?

Indeed, my goal is to have the base class of ABC determined dynamically
via a parameter passed into the constructor.
If you are trying to customize the base class of ABC by passing an
argument to the constructor of ABC, you should probably reconsider. If
constructing one instance of ABC can change ABC (the class) itself,
then the behavior of other instances will be affected as well. No
programmer can stay sane if he creates instances of a class that could
suddenly change its behavior (due to someone else's code).

Fortunately, the ABC class is not very useful. In fact, it was mostly
just to be used to store particular examples of the user-specified base
class. So all method calls would be from the base class only.
What you could do instead is to create a function which constructs
classes based on the arguments it recieves. Then, you'll be able to
create instances of the generated classes (all this meta-thinking is
huring my brain ;). I am talking about something like this:

def createClass(name, base):
exec "class %s(%s): pass" % (name, base)
return eval( "name" )

In fact, this is exactly what I ended up doing.

def example(base):
if base == SomeClass:
# call SomeClass.__init__(self)
# build example as it would look in SomeClass

elif base == SomeOtherClass:
# call SomeOtherClass.__init__(self)
# build example as it would in SomeOtherClass
Can you please tell us why you are doing this? My curiosity is killing
me!

So here is a good example:

I have 4 classes:

Lion(Animal):
Ant(Animal):
Bee(Animal):
Human(Animal):

which are all subclasses of some superclass called Animal. Now I want
to define an occupation. For example, Worker. A worker can exist as any
of the 4 classes above. Their constructors are different and I might
want to add certain features.

My first thought was to create a class called "Worker" and have the base
class determined by a variable which is passed into the constructor.
Most of the method calls will come from the Animal superclass anyway,
but some method calls might come from the Lion class, for example.

Now I realize this would drive a programmer crazy...because a Lion might
have a roar() method whereas a Human might have a holler() method. But
so long as the user knew which argument they passed in, it shouldn't be
too difficult to keep track of it.

So for example (again, I know what I am typing doesn't actually work)...

Worker(some_animal):
def __init__(self,some_animal):
# change the base class to some_animal

if some_animal == Lion:
# give the lion a big mane

if some_animal == Ant:
# make the ant dumb

if some_animal == Bee:
# make the bee know how to dance

if some_animal == Human
# give the human a hardhat

def work(self, hours):
# tell the animal to work for the specified number of hours
if some_animal == Lion:
self.cat_nap(hours)
if some_animal == Ant:
self.walk_back_and_forth(hours)
if some_animal == Bee:
self.buzz_and_dance(hours)
if some_animal == Human:
self.use_hammer_on_coworker(hours)
# notice, a Human does not have a cat_nap method

def take_lunch(location):

.....

and so on. So the thought is that a Worker can exist in many different
forms: as a lion, as an ant, as a bee, and as a human. And I might want
a single worker class that represents them all.

Hopefully this makes sense.
Another meta-thought: Hopefully I've beaten everyone else to the punch
about that question. Is it just me, or will a reply with such a
question always tell the original poster that what he wants to do MUST
be flawed? I hope I have been gentler than this.

:) There is no need to be too gentle. We are all here to learn (or
help). So I am fairly happy with the def solution...any comments on
this? But a Worker is an noun, and it seems like the proper way to do
this is to make the Worker into a class...so that I can define methods
like "work", "take_lunch", etc. However, I have no idea how I should do
this. Perhaps someone can recommend a procedure.

thanks.
 
D

Dennis Lee Bieber

I want a class that will determine its base class by the argument passed
in. What I am about to write _does_not_work_, but it shows what I am
trying to do.

class ABC(some_super):

This states that ABC is a subclass of "some_super", which probably
has to be in scope by this point of execution.
def __init__(self,some_super):

The __init__() function is an initializer... By the time it is
called, the class instance "self" has already been created. Maybe it is
the seldom seen __new__() that you need to overload -- so that it can
figure out what type of object to "create" as "self".

Or maybe this is where metaclasses come in -- I've not done anything
with metaclass work.
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
M

Maric Michaud

Le lundi 14 août 2006 09:33, Jackson a écrit :
Now I realize this would drive a programmer crazy...because a Lion might
have a roar() method whereas a Human might have a holler() method. But
so long as the user knew which argument they passed in, it shouldn't be
too difficult to keep track of it.

There are many problems if you go that way.
One implementation could be :

In [28]: class Animal(object) :
....: _types = {}
....:
....:

In [29]: class Worker(object) :
....: def work(self) : print 'hard'
....:
....:

In [30]: def createLion(*classes) :
....: def roar(self) : print "roar"
....: if not classes in Animal._types :
....: Animal._types[classes] = type('Lion', (Animal,) + classes,
{'roar' : roar})
....: return Animal._types[classes]()
....:

In [31]:

In [31]: createLion().roar()
roar

In [32]: type(createLion())
Out[32]: <class '__main__.Lion'>

In [33]: type(createLion()) is type(createLion())
Out[33]: True

In [34]: createLion(Worker).roar()
roar

In [35]: createLion(Worker).work()
hard

In [36]: type(createLion(Worker))
Out[36]: <class '__main__.Lion'>

In [37]: type(createLion(Worker)) is type(createLion(Worker))
Out[37]: True

In [38]: Animal._types
Out[38]:
{(): <class '__main__.Lion'>,
(<class '__main__.Worker'>,): <class '__main__.Lion'>}

In [39]: type(createLion(Worker)) is type(createLion())
Out[39]: False

The main problems are : first, your classes definition are hidden, second
there is no real Lion class to test, say isinstance(createLion(toto), Lion),
this can be misleading.

What you are trying to achieve is more commonly done by agregation and
delegation :

In [47]: class Lion(Animal) :
....: def __init__(self, *classes) :
....: self._objects = tuple(c() for c in classes)
....: def isA(self, class_) :
....: return class_ in (type(o) for o in self._objects)
....: def __getattr__(self, name) :
....: for obj in self._objects :
....: try: return getattr(obj, name)
....: except: pass
....: raise AttributeError('not defined or found in objects "%s"' %
name)
....:
....:

In [48]: Lion().work()
---------------------------------------------------------------------------
exceptions.AttributeError Traceback (most recent
call last)

/home/maric/<ipython console>

/home/maric/<ipython console> in __getattr__(self, name)

AttributeError: not defined or found in objects "work"

In [49]: Lion().isA(Worker)
Out[49]: False

In [50]: Lion(Worker).isA(Worker)
Out[50]: True

In [51]: Lion(Worker).work()
hard



--
_____________

Maric Michaud
_____________

Aristote - www.aristote.info
3 place des tapis
69004 Lyon
Tel: +33 426 880 097
 
J

John Machin

Jackson said:
I have 4 classes:

Lion(Animal):
Ant(Animal):
Bee(Animal):
Human(Animal):

which are all subclasses of some superclass called Animal. Now I want
to define an occupation. For example, Worker. A worker can exist as any
of the 4 classes above. Their constructors are different and I might
want to add certain features.

My first thought was to create a class called "Worker" and have the base
class determined by a variable which is passed into the constructor.
Most of the method calls will come from the Animal superclass anyway,
but some method calls might come from the Lion class, for example.

Here are a couple of thoughts that *might* help:

(1) mix-in i.e. a class can have multiple base classes:

class AntWorker(Animal, Worker):

(2) you can create classes on the fly using the 3-argument form of the
built-in type() function:

new_cls = type(name_of_class, base_classes_tuple, dict_of_methods_etc)

so you can have just one base class (i.e. object) and populate the dict
with methods and/or you can have pre-packaged base-classes each already
containing relevant methods ... so when the order for a new tailored
class comes in you just rush about your virtual warehouse throwing
packaged base-classes into a tuple and extra methods into a dict,
attach a name tag and then type() gift-wraps it for you.

You need to read up on "method resolution order" aka "mro" before
planning what methods go in what mixin classes and what methods are
optional add-ons and what order you specify the classes in the tuple.

HTH,
John
 
J

Jackson

Maric Michaud wrote the following on 2006-08-14 01:26:
In [28]: class Animal(object) :
....: _types = {}
....:
....:

In [29]: class Worker(object) :
....: def work(self) : print 'hard'
....:
....:
[snip]
What you are trying to achieve is more commonly done by agregation and
delegation :

In [47]: class Lion(Animal) :
....: def __init__(self, *classes) :
....: self._objects = tuple(c() for c in classes)
....: def isA(self, class_) :
....: return class_ in (type(o) for o in self._objects)
....: def __getattr__(self, name) :
....: for obj in self._objects :
....: try: return getattr(obj, name)
....: except: pass
....: raise AttributeError('not defined or found in objects "%s"' %
name)
....:
....:

In [48]: Lion().work()
---------------------------------------------------------------------------
exceptions.AttributeError Traceback (most recent
call last)

/home/maric/<ipython console>

/home/maric/<ipython console> in __getattr__(self, name)

AttributeError: not defined or found in objects "work"

In [49]: Lion().isA(Worker)
Out[49]: False

In [50]: Lion(Worker).isA(Worker)
Out[50]: True

In [51]: Lion(Worker).work()
hard

This is exactly what I am looking for. However, I am not sure how to
implement different Worker methods. For example, a Lion might work
differently than an Bee. In my example, the Lion would take a cat-nap
while the Bee might do a dance.

It seems that I would need to what kind of class called the work()
method. Is there a way to do that?

Even if I could do that, it seems these various definitions of work
should probably go into the class of the animal---so that Lion actions
are all within the Lion class. Thus, the Lion class should have its own
work method, and the Bee class should have its own work method as well.
The problem with this is that every Lion can use the work method, when
I really only work Workers to use the work method.

I can picture another way of achieving this...have a list of
occupations...which are booleans for each instance of the class. Then
the work() method will call only if the Worker boolean is True. This
should be sufficient...and the differing work methods would be in their
respective classes. However, now the actual method names are not
uniform---that is, it becomes a bookkeeping exercise to remember that
when Worker is True, then the method to create is work(), that when
Student is True, then the method to create is study(). So this
procedure has its own problems too. It seems like I am trading off
hardships now.

So here is what I am looking for:

A single Worker class with a standardized set of method names. The
methods in the Worker class are dependent on the "superclass" (via
aggregation and delegation, as shown above) of the worker. That is, a
Bee performs different actions when working than a Lion or a Human. And
finally, the occupations such that "not every Bee is a worker" and
"there are some Workers which are Bees".

Thanks!
 
J

Jackson

John Machin wrote the following on 2006-08-14 01:45:
Here are a couple of thoughts that *might* help:

(1) mix-in i.e. a class can have multiple base classes:

class AntWorker(Animal, Worker):

(2) you can create classes on the fly using the 3-argument form of the
built-in type() function:

new_cls = type(name_of_class, base_classes_tuple, dict_of_methods_etc)

This seems like it should work. The only problem I have with it is that
there are a _lot_ of classes to define (and remember). For 256
different animals, we'd have to create 256 more animal-worker classes.
Obviously this works, but it seems not to be in the spirit of classes
and subclasses in that it doesn't (in some central way) highlight that
there is just one occupation: a worker.

As another example, suppose we had Shakespeare's Romeo and Juliet. What
I mean is that we have the "idea" (the storyline, themes, etc) of Romeo
and Juliet. Further, suppose that we have various ways of expressing the
piece: poem, book, movie, broadway.

It is true, we could do:

a = RaJPoem()
a = RaJBook()
a = RaJMovie()
a = RaJBroadway()

but it would be nice if we could do something like this:

a = RaJ(Poem)
a = RaJ(Book)
a = RaJ(Movie)
a = RaJ(Broadway)

And then a method call to RaJ might do something different for each
media. For example,

a.get_opening()

should fetch the opening of each media(first stanza, first chapter,
first scene, etc). Thus, Poem, Book, Movie, and Broadway should
probably have a get_opening() method, and the RaJ class should pass this
call onto the respective class. Notice, get_opening() is not exclusive
to RaJ. Maric's method works nicely for this.

Additionally, I might want some methods which are exclusive to RaJ. For
example,

a.get_love()

would fetch elements of love from each type of media. Clearly this
method depends on the type of media. And there is not one method that
each media class could call.

For a poem, the RaJ class might look for a specific way that love can be
expressed (specific to RaJ). Studpid example, look for the word 'love'
at the end of each line.

For a movie, we might look for any scenes where the couple kisses.

The point is that there are methods for which the set of calls will
differ depending on the media type (poem, book, movie, etc).

This seems like a fun idea to me, and I'd like to think that things like
this are done frequently.

Thanks.
 
D

danielx

Jackson said:
Thanks for the reply.

danielx wrote the following on 2006-08-13 19:49:

Indeed, my goal is to have the base class of ABC determined dynamically
via a parameter passed into the constructor.

Fortunately, the ABC class is not very useful. In fact, it was mostly
just to be used to store particular examples of the user-specified base
class. So all method calls would be from the base class only.


In fact, this is exactly what I ended up doing.

def example(base):
if base == SomeClass:
# call SomeClass.__init__(self)
# build example as it would look in SomeClass

elif base == SomeOtherClass:
# call SomeOtherClass.__init__(self)
# build example as it would in SomeOtherClass


So here is a good example:

I have 4 classes:

Lion(Animal):
Ant(Animal):
Bee(Animal):
Human(Animal):

which are all subclasses of some superclass called Animal. Now I want
to define an occupation. For example, Worker. A worker can exist as any
of the 4 classes above. Their constructors are different and I might
want to add certain features.

My first thought was to create a class called "Worker" and have the base
class determined by a variable which is passed into the constructor.

What you should keep in mind though, is the "is-a" relationship that
subclasses are supposed to have with their parents. "worker is a lion"
does not make sense. What you want instead is LionWorker. You'll end up
doing only a little more typing than if you had a general Worker class,
but I don't think it will be much more.

If your *Worker classes have really simple bodies, you might be able to
automate the creation of your *Worker classes by doing something like
the createClass function (ie, by creating the classes dynamically
instead of using the class statement).

I also discovered another way to dynamically create classes, described
in this article (the article talks about other things as well):

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html

I'm not sure if this will help you, but it's definitely interesting.
Most of the method calls will come from the Animal superclass anyway,
but some method calls might come from the Lion class, for example.

Now I realize this would drive a programmer crazy...because a Lion might
have a roar() method whereas a Human might have a holler() method. But
so long as the user knew which argument they passed in, it shouldn't be
too difficult to keep track of it.

So for example (again, I know what I am typing doesn't actually work)...

Worker(some_animal):
def __init__(self,some_animal):
# change the base class to some_animal

if some_animal == Lion:
# give the lion a big mane

if some_animal == Ant:
# make the ant dumb

if some_animal == Bee:
# make the bee know how to dance

if some_animal == Human
# give the human a hardhat

def work(self, hours):
# tell the animal to work for the specified number of hours
if some_animal == Lion:
self.cat_nap(hours)
if some_animal == Ant:
self.walk_back_and_forth(hours)
if some_animal == Bee:
self.buzz_and_dance(hours)
if some_animal == Human:
self.use_hammer_on_coworker(hours)
# notice, a Human does not have a cat_nap method

def take_lunch(location):

....

and so on. So the thought is that a Worker can exist in many different
forms: as a lion, as an ant, as a bee, and as a human. And I might want
a single worker class that represents them all.

Hopefully this makes sense.


:) There is no need to be too gentle. We are all here to learn (or
help). So I am fairly happy with the def solution...any comments on

True, but that doesn't mean you have to stand a rude response. I'm just
hoping for a little more courtesy than what I've seen.
 
D

danielx

Jackson said:
Maric Michaud wrote the following on 2006-08-14 01:26:
In [28]: class Animal(object) :
....: _types = {}
....:
....:

In [29]: class Worker(object) :
....: def work(self) : print 'hard'
....:
....:
[snip]
What you are trying to achieve is more commonly done by agregation and
delegation :

In [47]: class Lion(Animal) :
....: def __init__(self, *classes) :
....: self._objects = tuple(c() for c in classes)
....: def isA(self, class_) :
....: return class_ in (type(o) for o in self._objects)
....: def __getattr__(self, name) :
....: for obj in self._objects :
....: try: return getattr(obj, name)
....: except: pass
....: raise AttributeError('not defined or found in objects "%s"' %
name)
....:
....:

In [48]: Lion().work()
---------------------------------------------------------------------------
exceptions.AttributeError Traceback (most recent
call last)

/home/maric/<ipython console>

/home/maric/<ipython console> in __getattr__(self, name)

AttributeError: not defined or found in objects "work"

In [49]: Lion().isA(Worker)
Out[49]: False

In [50]: Lion(Worker).isA(Worker)
Out[50]: True

In [51]: Lion(Worker).work()
hard

This is exactly what I am looking for. However, I am not sure how to
implement different Worker methods. For example, a Lion might work
differently than an Bee. In my example, the Lion would take a cat-nap
while the Bee might do a dance.

It seems that I would need to what kind of class called the work()
method. Is there a way to do that?

If all of your animals are supposed to be workers, maybe you should
declare your animal classes like this:

class Lion(Animal, Worker):
def work(self): pass

Your Worker class might not have a work method (or it might have one
which does no work, pun intended), even though the methods it does have
depend on an instance being able to respond to a work call. Then, the
Worker class is like an "abstract" class in Java (cross yourselves).
This is an example of "delegation" which someone here has already
mentioned. In that case, users will most likely never instantiate
Worker, but none of the Worker code needs to be replicated, because
Lion (or whatever) is a subclass of Worker. This is one of the
"benefits" of OOP :p.
Even if I could do that, it seems these various definitions of work
should probably go into the class of the animal---so that Lion actions
are all within the Lion class. Thus, the Lion class should have its own
work method, and the Bee class should have its own work method as well.
The problem with this is that every Lion can use the work method, when
I really only work Workers to use the work method.

I can picture another way of achieving this...have a list of
occupations...which are booleans for each instance of the class. Then
the work() method will call only if the Worker boolean is True. This
should be sufficient...and the differing work methods would be in their
respective classes. However, now the actual method names are not
uniform---that is, it becomes a bookkeeping exercise to remember that
when Worker is True, then the method to create is work(), that when
Student is True, then the method to create is study(). So this
procedure has its own problems too. It seems like I am trading off
hardships now.

So here is what I am looking for:

A single Worker class with a standardized set of method names. The

In Java (cross-ing ritual), you would create an interface, which
requires a work method. But this is Python :p. We just don't do that.
Search the following page for "easier to ask for forgiveness" and "look
before you leap" on this page (the glossary of Guido's Python
tutorial):

http://docs.python.org/tut/node18.html
 

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
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top