Play with classes

Z

Zunbeltz Izaola

Hi to all!

I wonder if it possible (i'm sure python can do :) ) to define classes on
runtime. My problem (schematically) is the folowwin.

The User can choise betwenn 3 property of an object.

Mode, Type and Subtype.

I have the following classes defined

ModeA, ModeB, TypeA, TypeB, TypeC, SubtypeA, SubtypeB.

Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
something like

class UserClass(ModeA, TypeB, SubtypeB):
pass

I can define all the posibilitys different classes and the using nested
if/else I can use the correct class, but I want to know if there is a way
generate in the fly and in this way there is no necesarity to change code whe
new Modes or Types are created.

I hope it is clear enough :)

Thanks in advance

Zunbeltz
 
P

Peter Otten

Zunbeltz said:
Hi to all!

I wonder if it possible (i'm sure python can do :) ) to define classes on
runtime. My problem (schematically) is the folowwin.

The User can choise betwenn 3 property of an object.

Mode, Type and Subtype.

I have the following classes defined

ModeA, ModeB, TypeA, TypeB, TypeC, SubtypeA, SubtypeB.

Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
something like

class UserClass(ModeA, TypeB, SubtypeB):
pass

I can define all the posibilitys different classes and the using nested
if/else I can use the correct class, but I want to know if there is a way
generate in the fly and in this way there is no necesarity to change code
whe new Modes or Types are created.

I hope it is clear enough :)

Thanks in advance

Zunbeltz

How about assigning to __bases__?
.... def alpha(self): print "alpha"
........ def beta(self): print "beta"
....
Trying the same thing with newstyle class resulted in a TypeError:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: __bases__ assignment: 'A' deallocator differs from 'object'

Peter
 
J

Jacek Generowicz

Zunbeltz Izaola said:
I have the following classes defined

ModeA, ModeB, TypeA, TypeB, TypeC, SubtypeA, SubtypeB.

Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
something like

class UserClass(ModeA, TypeB, SubtypeB):
pass

I can define all the posibilitys different classes and the using nested
if/else I can use the correct class, but I want to know if there is a way
generate in the fly and in this way there is no necesarity to change code whe
new Modes or Types are created.

Is the following what you want ?

======================================================
class TypeA: pass
class TypeB: pass
class TypeC: pass
class SubtypeA: pass
class SubtypeB: pass

collect_bases = [(Mode, Type, Subtype)
for Mode in [ModeA, ModeB]
for Type in [TypeA, TypeB, TypeC]
for Subtype in [SubtypeA, SubtypeB]]

count = 0
for bases in collect_bases:
name = "UserClass%d" % count
the_class = type(name, bases, {})
globals()[name] = the_class
count += 1
======================================================

Now you can try
[QUOTE= said:
UserClass2.__bases__
[/QUOTE][/QUOTE]
(<class '__main__.ModeA'>, <class '__main__.TypeB'>, <class
'__main__.SubtypeA'>)

And so on, until
(<class '__main__.ModeB'>, <class '__main__.TypeC'>, <class
'__main__.SubtypeB'>)
 
J

Jacek Generowicz

Jacek Generowicz said:
======================================================
class TypeA: pass
class TypeB: pass
class TypeC: pass
class SubtypeA: pass
class SubtypeB: pass

collect_bases = [(Mode, Type, Subtype)
for Mode in [ModeA, ModeB]
for Type in [TypeA, TypeB, TypeC]
for Subtype in [SubtypeA, SubtypeB]]

count = 0
for bases in collect_bases:
name = "UserClass%d" % count
the_class = type(name, bases, {})
globals()[name] = the_class
count += 1
======================================================

Oooops, the first 4 lines got lost:

======================================================
__metaclass__ = type

class ModeA: pass
class ModeB: pass
class TypeA: pass
class TypeB: pass
class TypeC: pass
class SubtypeA: pass
class SubtypeB: pass

collect_bases = [(Mode, Type, Subtype)
for Mode in [ModeA, ModeB]
for Type in [TypeA, TypeB, TypeC]
for Subtype in [SubtypeA, SubtypeB]]

count = 0
for bases in collect_bases:
name = "UserClass%d" % count
globals()[name] = type(name, bases, {})
count += 1
======================================================
 
Z

Zunbeltz Izaola

Thank for the help.
Oooops, the first 4 lines got lost:

======================================================
__metaclass__ = type

I am not an expert and i don't understand very well things like
__metaclass__, but it not redundat this line? From the Language Reference

"""
__metaclass__
This variable can be any callable accepting arguments for name, bases,
and dict. Upon class creation, the callable is used instead of the
built-in type(). New in version 2.2.
"""

so I think __metaclass__ = type is the same as not defining __metaclass__

Regards,

Zunbeltz
class ModeA: pass
class ModeB: pass
class TypeA: pass
class TypeB: pass
class TypeC: pass
class SubtypeA: pass
class SubtypeB: pass

collect_bases = [(Mode, Type, Subtype)
for Mode in [ModeA, ModeB]
for Type in [TypeA, TypeB, TypeC]
for Subtype in [SubtypeA, SubtypeB]]

count = 0
for bases in collect_bases:
name = "UserClass%d" % count
globals()[name] = type(name, bases, {})
count += 1
======================================================
 
T

Terry Reedy

Zunbeltz Izaola said:
Hi to all!

I wonder if it possible (i'm sure python can do :) ) to define classes on
runtime. My problem (schematically) is the folowwin.

The class statement, like all statements except the global directive, it a
runtime executable statement. So, in a sense, all class objects are
defined (created) at runtime. So you are perhaps asking, "Can I write a
class statement at runtime (using user input)?" If so, yes. Create a
string with the code you want executed, then exec it with an exec
statement. Or you can use the approach others suggested of interpreting
user input to build up a class object. Your choice.

Terry J. Reedy
 
M

Mike C. Fletcher

As another poster noted, all classes are created at run-time. There's
even a hook that let's you intercept the creation of a class called the
"metaclass hook", however, to deal with the question directly before I
go off on a tangent...

There are two major approaches you can take to elegantly composing
classes at run-time; Factory functions and metaclasses. Here's an
example of the factory function approach, (which is probably most
appropriate in your case (user is only creating new classes via GUI
interactions, no need for ability to sub-class in Python code, no need
for addition of newly-created methods/functions, i.e. straight
composition)):

def createClass( name, baseClasses, dictionary ):
"""Create a new class object and return it"""
# reshuffle baseClasses here
# manipulate dictionary here
# check that name is unique here
# register with some global registry here
# register pickle helpers here
if definitions.has_key( uniqueFingerprint):
return that
else:
# if you were paying attention, you'll notice
# that save for the manipulation comments we
# just call this...
return type( name, baseClasses, dictionary )

If, however, your users may *also* want to define these classes as part
of Python modules (for an extension mechanism), you may want to subclass
"type" to encode your registration/reshuffling/manipulation/etc.
directly and then use that metaclass (sub-class of type) for each of
your classes:

class MetaFoo( type ):
"""Metaclass for the example code"""
definitions = {}
def __new__( metacls, name, bases, dictionary ):
# reshuffle baseClasses here
# manipulate dictionary here
# check that name is unique here
uniqueFingerprint = name, bases
if metacls.definitions.has_key( uniqueFingerprint ):
# Note: this likely *isn't* a good idea, as it can really
# surprise your users to discover that their classes
# are silently unified with another class! Just a demo...
return metacls.definitions.get( uniqueFingerprint )
else:
result = super(MetaFoo,metacls).__new__(
metacls, name, bases, dictionary
)
metacls.definitions[ uniqueFingerprint ] = result
# register with some global registry here
# register pickle helpers here
return result

__metaclass__ = MetaFoo
class ModeA:
pass

class ModeB:
pass

class ModeC( ModeA, ModeB ):
pass
class ModeD( ModeA, ModeB ):
pass

print MetaFoo( 'A', (), {} ) is MetaFoo( 'A', (), {} )

As noted in the comments above, likely you don't even want the effect of
having the class-cache (too confusing for users, mostly, but I wanted
some sort of manipulation to stick in to say "something happens here" :)
), so there's no particular value to the metaclass version for your
case. I just wanted an opportunity to work on an example for my talk...
I'm sick, I know...

Some things to keep in mind:

* Your new class instances will *not* be pickle-able (using either
method) unless they are registered explicitly somewhere in an
importable module. You will need to figure out how to ensure
that, on unpickling, your newly-created classes are available
(e.g. by storing their definitions in a database somewhere and
hooking import to treat the DB as a module).
* metaclasses are more fun :) , but they take some getting used to,
and are probably overkill for this simple excursion into
metaprogramming
* The "new" module has an (ironically) older API for creating class
objects

Have fun,
Mike

Zunbeltz said:
Hi to all!

I wonder if it possible (i'm sure python can do :) ) to define classes on
runtime. My problem (schematically) is the folowwin.

The User can choise betwenn 3 property of an object.

Mode, Type and Subtype.

I have the following classes defined

ModeA, ModeB, TypeA, TypeB, TypeC, SubtypeA, SubtypeB.

Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
something like

class UserClass(ModeA, TypeB, SubtypeB):
pass

I can define all the posibilitys different classes and the using nested
if/else I can use the correct class, but I want to know if there is a way
generate in the fly and in this way there is no necesarity to change code whe
new Modes or Types are created.
....
 
M

Michele Simionato

Peter Otten said:
Trying the same thing with newstyle class resulted in a TypeError:

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: __bases__ assignment: 'A' deallocator differs from 'object'

You can't do that with new style classes! I guess because of some
subtle issue with metaclasses, but I don't really know.

The OP needs "type", the custom metaclass:

UserClass=type("UserClass",(ModeA, TypeB, SubtypeB),{})


Michele Simionato
 
D

David M. Cook

Supose the user whant to combine ModeA with TypeB and SubtypeB, so I need
something like

class UserClass(ModeA, TypeB, SubtypeB):


You can use the type builtin (2.2 and above) to create a class type
dynamically. Syntax is

type(name_string, bases_tuple, methods_dict)

For example:

In [68]: foo = type('Foo', (object,), {})
In [69]: foo.mro()
Out[69]: [<class '__main__.Foo'>, <type 'object'>]

Dave Cook
 
J

Jacek Generowicz

Zunbeltz Izaola said:
Thank for the help.

I hope that it is what you were asking for.

Of course, my posting such a solution should in no way be interpreted
as a suggestion that it is appropriate to your _real_ problem. You may
well want a completely different approach, but I can't tell without
knowing more about your application.
I am not an expert and i don't understand very well things like
__metaclass__, but it not redundat this line? From the Language Reference

"""
__metaclass__
This variable can be any callable accepting arguments for name, bases,
and dict. Upon class creation, the callable is used instead of the
built-in type(). New in version 2.2.
"""

so I think __metaclass__ = type is the same as not defining __metaclass__

It is indeed tempting to conclude that from what you have quoted.

One of the beauties of Python is its highly interactive nature, and
the ease with which you can try things out. Your hypothesis that
"__metaclass__ = type is the same as not defining __metaclass__" can
be refuted by the Python interpreter itself within about 10 seconds of
work. Take the code I sent, remove the binding of __metaclass__ and
run the code. You will find that Python replies:

TypeError: a new-style class can't have only classic bases

As written in my example, ("class ModeA: pass" etc.) all the classes
are classic, because they do not inherit from object.
<type 'type'>

I want them to be new-style classes, because I am going to create new
classes which inherit from them, using type. This means of creating
classes creates new-style classes, and new style classes, as the error
message above suggests, "can't have only classic bases".

So, I could either make ModeA & co inherit from object, or I could
make all classes new-style ones by default, by binding __metaclass__
to type.

Alternatively, I could not use type to create the UserClasses, but
types.ClassType (types is a module). Alternatively I could use use
type(ModeA) which would pick the appropriate metaclass for creation of
your UserClasses depending on the situation.
"""
__metaclass__
This variable can be any callable accepting arguments for name, bases,
and dict. Upon class creation, the callable is used instead of the
built-in type(). New in version 2.2.
"""

Hmm. Is that a documentation bug? I suspect that it should read "
.... instead of types.ClassType"


[ So, here's a classic class version of the original:

class ModeA: pass
class ModeB: pass
class TypeA: pass
class TypeB: pass
class TypeC: pass
class SubtypeA: pass
class SubtypeB: pass

collect_bases = [(Mode, Type, Subtype)
for Mode in [ModeA, ModeB]
for Type in [TypeA, TypeB, TypeC]
for Subtype in [SubtypeA, SubtypeB]]

count = 0
for bases in collect_bases:
name = "UserClass%d" % count
globals()[name] = type(ModeA)(name, bases, {})
# or you could "import types" and do the following
# globals()[name] = types.ClassType(name, bases, {})
count += 1

]
 

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