Create a class at run-time

M

Michel

Hi everyone,

I'm trying to dynamically create a class. What I need is to define a
class, add methods to it and later instantiate this class. Methods
need to be bound to the instance though, and that's my problem. Here
is what I have so far:

method_template = "def test_foo(self):\
#actual test_foo\
pass"
exec method_template

TestClass = types.ClassType("MyTestClass", (unittest.TestCase, ), {})
TestClass.__module__ = "test"

now what to do next?
I looked at types.MethodType but it needs an instance to bind the
method and a function object.
Should I define __new__ to bind the method during instantiation?

Hope this makes sense,

Michel.
 
M

Michiel Overtoom

I'm trying to dynamically create a class. What I need is to define a
class, add methods to it and later instantiate this class. Methods
need to be bound to the instance though, and that's my problem.

Maybe this snippet is of any help?

import functools

class Template(object):
pass

def printmyname(self):
print self.name

t=Template()
t.name="Pete"
t.printmyname=functools.partial(printmyname,t)

u=Template()
u.name="Mary"
u.printmyname=functools.partial(printmyname,u)

t.printmyname()
u.printmyname()

Greetings,
 
P

Patrick Maupin

Hi everyone,

I'm trying to dynamically create a class. What I need is to define a
class, add methods to it and later instantiate this class. Methods
need to be bound to the instance though, and that's my problem. Here
is what I have so far:

Well, you should just fill your empty dict with function definitions,
BEFORE you build the class. That's easiest. Also, you can just use
type:

def foo(*whatever):
print foo

bar = type('MyDynamicClass', (object,), dict(foo=foo))

HTH,
Pat
 
M

Michel

Well, I don't have the reference to the instance. The class is
actually instantiated later by a the unittest library.
 
M

Michel

I want to add a method to a class such that it can be invoked on
specifics instances.
You solution works (as well as Patrick's one), thanks !
I still have a question though. If I print the type of the self object
I get when my method is
called, I get "<class 'test.TestClass'>". I guess this is because the
method is defined as a class method.
This is ok in my case, but just out of curiosity, what should I do to
change this method to an instance method?

Thanks for your help,

Michel.
 
P

Peter Otten

Michel said:
Hi everyone,

I'm trying to dynamically create a class. What I need is to define a
class, add methods to it and later instantiate this class. Methods
need to be bound to the instance though, and that's my problem. Here
is what I have so far:

method_template = "def test_foo(self):\
#actual test_foo\
pass"
exec method_template

TestClass = types.ClassType("MyTestClass", (unittest.TestCase, ), {})
TestClass.__module__ = "test"

now what to do next?

Just assign it:
.... self.assertEquals(1, 2)
....F
======================================================================
FAIL: test_foo (__main__.MyTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
File "<stdin>", line 2, in test_foo
AssertionError: 1 != 2

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

If you don't know the method name beforehand use

setattr(MyTestClass, method_name, method), e. g:
.... def test(self): self.assertEquals(2, n)
.... return test
........ setattr(MyTestClass, "test_%d" % i, make_method(i))
....FF.
======================================================================
FAIL: test_0 (__main__.MyTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
File "<stdin>", line 2, in test
AssertionError: 2 != 0

======================================================================
FAIL: test_1 (__main__.MyTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
File "<stdin>", line 2, in test
AssertionError: 2 != 1

----------------------------------------------------------------------
Ran 3 tests in 0.000s

FAILED (failures=2)

Peter
 
M

Michel

Thanks Peter.

I searched a little bit more and wrote the following example:

------------------------------------
import types

class MyClass:

def test_toto(self):
print type(self)
print self.name

def test_toto(self):
print type(self)
print self.name

MyDynClass = types.ClassType("MyDynClass", (object, ), {})
MyDynClass.__module__ = "test.complex.hierarchy"
MyDynClass.test_toto = test_toto

t1 = MyDynClass()
t2 = MyDynClass()

t1.name = "Marcel"
t2.name = "Oscar"

t1.test_toto()
t2.test_toto()

c1 = MyClass()
c1.name = "Raoul"
c1.test_toto()
--------------------------------

the output is:

<class 'test.complex.hierarchy.MyDynClass'>
Marcel
<class 'test.complex.hierarchy.MyDynClass'>
Oscar
<type 'instance'>
Raoul

I'm wondering why the type of the self parameter is not 'instance' in
the calls
t1.test_toto() and t2.test_toto()

The rest of the behavior is correct though, so I guess it's just
internal Python stuff.

Thanks for your help,

Michel.
 
P

Peter Otten

Michel said:
Thanks Peter.

I searched a little bit more and wrote the following example:

------------------------------------
import types

class MyClass:

def test_toto(self):
print type(self)
print self.name

def test_toto(self):
print type(self)
print self.name

MyDynClass = types.ClassType("MyDynClass", (object, ), {})
MyDynClass.__module__ = "test.complex.hierarchy"
MyDynClass.test_toto = test_toto

t1 = MyDynClass()
t2 = MyDynClass()

t1.name = "Marcel"
t2.name = "Oscar"

t1.test_toto()
t2.test_toto()

c1 = MyClass()
c1.name = "Raoul"
c1.test_toto()
--------------------------------

the output is:

<class 'test.complex.hierarchy.MyDynClass'>
Marcel
<class 'test.complex.hierarchy.MyDynClass'>
Oscar
<type 'instance'>
Raoul

I'm wondering why the type of the self parameter is not 'instance' in
the calls
t1.test_toto() and t2.test_toto()

The rest of the behavior is correct though, so I guess it's just
internal Python stuff.

In Python 2.x there are "classic" and "newstyle" classes. In practice the
main differences are that classic classes are more likely to call
__getattr__() and that only newstyle classes support properties correctly.

By inheriting from object you make MyDynClass a newstyle class:
(<class '__main__.A'>, <type 'type'>)

Classic classes exist for backwards compatibility and because most
programmers are too lazy to have their classes inherit from object when the
difference doesn't matter. When you create a class dynamically I recommend
that you use the type() builtin instead of types.ClassType(). This will
always create a newstyle class -- even when you don't inherit from object
explicitly:
(<type 'object'>,)

Peter
 
S

Steve Holden

Michel said:
Thanks Peter.

I searched a little bit more and wrote the following example:

------------------------------------
import types

class MyClass:

def test_toto(self):
print type(self)
print self.name

def test_toto(self):
print type(self)
print self.name

MyDynClass = types.ClassType("MyDynClass", (object, ), {})
MyDynClass.__module__ = "test.complex.hierarchy"
MyDynClass.test_toto = test_toto

t1 = MyDynClass()
t2 = MyDynClass()

t1.name = "Marcel"
t2.name = "Oscar"

t1.test_toto()
t2.test_toto()

c1 = MyClass()
c1.name = "Raoul"
c1.test_toto()
--------------------------------

the output is:

<class 'test.complex.hierarchy.MyDynClass'>
Marcel
<class 'test.complex.hierarchy.MyDynClass'>
Oscar
<type 'instance'>
Raoul

I'm wondering why the type of the self parameter is not 'instance' in
the calls
t1.test_toto() and t2.test_toto()

The rest of the behavior is correct though, so I guess it's just
internal Python stuff.
Yes, it's just that MyClass is an old-style class (its type is <type
'classobj'>) whereas MyDynClass is a new-style class (its type is <type
'type'>, because it inherits from object).

It's as though you had written

class MyClass:
...

class MyDynClass(object):
...

regards
Steve
 

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,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top