From: Michel on
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.

On Mar 25, 9:53 pm, I V <ivle...(a)gmail.com> wrote:
> On Thu, 25 Mar 2010 15:00:35 -0700, Michel wrote:
> > 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:
>
> I'm not entirely sure what you mean by binding methods to the instance.
> Do you mean you need to dynamically add methods to a specific instance?
> Or that you need to add methods to a class, such that they can be invoked
> on specific instances? For the latter, just do:
>
> TestClass.test_foo = test_foo
>
> For the former, try:
>
> tc = TestClass()
> tc.test_foo = types.MethodType(test_foo, tc)

From: Peter Otten on
Michel wrote:

> 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:

>>> import unittest
>>> class MyTestClass(unittest.TestCase): pass
....
>>> def test_foo(self):
.... self.assertEquals(1, 2)
....
>>> MyTestClass.test_foo = test_foo # <----
>>> unittest.main()
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:

>>> import unittest
>>> class MyTestClass(unittest.TestCase): pass
....
>>> def make_method(n):
.... def test(self): self.assertEquals(2, n)
.... return test
....
>>> for i in range(3):
.... setattr(MyTestClass, "test_%d" % i, make_method(i))
....
>>> unittest.main()
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

From: Michel on
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.

On Mar 26, 1:29 pm, Peter Otten <__pete...(a)web.de> wrote:
> Michel wrote:
> > 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:
>
> >>> import unittest
> >>> class MyTestClass(unittest.TestCase): pass
> ...
> >>> def test_foo(self):
>
> ...     self.assertEquals(1, 2)
> ...>>> MyTestClass.test_foo = test_foo # <----
> >>> unittest.main()
>
> 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:
>
> >>> import unittest
> >>> class MyTestClass(unittest.TestCase): pass
>
> ...                                          >>> def make_method(n):
>
> ...     def test(self): self.assertEquals(2, n)
> ...     return test                            
> ...>>> for i in range(3):
>
> ...     setattr(MyTestClass, "test_%d" % i, make_method(i))
> ...>>> unittest.main()
>
> 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

From: Peter Otten on
Michel wrote:

> 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:

>>> classic = types.ClassType("A", (), {})
>>> newstyle = types.ClassType("A", (object,), {})

>>> type(classic()), type(classic)
(<type 'instance'>, <type 'classobj'>)

>>> type(newstyle()), type(newstyle)
(<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(type("A", (), {}))
<type 'type'>
>>> type("A", (), {}).__bases__
(<type 'object'>,)

Peter

From: Steve Holden on
Michel wrote:
> 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
--
Steve Holden +1 571 484 6266 +1 800 494 3119
See PyCon Talks from Atlanta 2010 http://pycon.blip.tv/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/