4

How can I create classes dynamically in a class? As I tried below, I get a NameError: name 'Foo is not defined. I am quite new to Python, pls forgive me if it's obvious.

class Parent(object):
    name2class = {'foo' : Foo, 'bar' : Bar }

    def do(self,string):
        return name2class[string]()

class Foo(Parent):
    pass

class Bar(Parent):
    pass

if __name__ == '__main__':
    parent = Parent()
    instance = parent.do()
mr.soroush
  • 1,110
  • 2
  • 14
  • 31
uloco
  • 2,283
  • 4
  • 22
  • 37

4 Answers4

3

As you've written this, you need to define Foo and Bar above Parent since Parent references the other classes.

Edit: You just need to move those class references into the method:

class Parent(object):
    def do(self,string):
        name2class = {'foo' : Foo, 'bar' : Bar }
        return name2class[string]()

class Foo(Parent):
    pass

class Bar(Parent):
    pass

if __name__ == '__main__':
    parent = Parent()
    instance = parent.do('foo')

Edit2: Here's your factory version:

class Parent(object):
    # Add shared methods here
    pass

class Foo(Parent):
    # Add unique methods
    pass

class Bar(Parent):
    # Add unique methods
    pass

class ParentFactory(object):
    def __init__(self):
        self.name2class = {'foo' : Foo, 'bar' : Bar}

    def create(self, string):
        return self.name2class[string]()

if __name__ == '__main__':
    factory = ParentFactory()
    instance = factory.create('foo')
Kevin Stone
  • 8,831
  • 41
  • 29
  • In that case, he won't be able to inherit from Parent. – Alexander Zhukov Oct 17 '13 at 08:51
  • Btw, this really makes almost no sense as a construct. I was really just answering to show that in python, class definitions are expressions, so variable scoping rules apply. If you want to explain how you were trying to use this dynamic resolution (factory pattern?), I could help you find a better approach. – Kevin Stone Oct 17 '13 at 08:57
  • Thank you for your answers. My porblem is as it follows: I get a telgram from my client as a string. In this string, there is a section were there stands which kind of telgram it is ( there are a few and could get more in future) and in case of this information I want to create a class with its specific methods, inherited from parent, because the telegrams are often quite similar – uloco Oct 17 '13 at 09:01
  • @KevinStone: in Python `class` is actually a statement, not an expression. – bruno desthuilliers Oct 17 '13 at 09:01
  • @brunodesthuilliers good catch. Yes, its a statement not an expression. – Kevin Stone Oct 17 '13 at 09:03
  • @drrtyrokka Yep, sounds like a factory pattern to me. Usually, you have a separate class like `ParentFactory` that has a method like `create(string)` that returns the child class requested. All the shared methods, and attributes are in the `Parent`. – Kevin Stone Oct 17 '13 at 09:04
  • In this case the `ParentFactory` class is just overkill - a plain old function will work as well. – bruno desthuilliers Oct 17 '13 at 09:11
  • @brunodesthuilliers Obviously. But I figured this was an OOP assignment, so I took a flyer that his/her prof wanted to see classes :) – Kevin Stone Oct 17 '13 at 09:14
  • yea, i'm writing on my bachelor thesis :P Thanks very much!! – uloco Oct 17 '13 at 09:17
  • @KevinStone + Can your explain the reason, why OP's version doesn't works? It will be very helpful for me. Although I have idea that in OP version `name2class` inits when class loaded (belongs to class). – Grijesh Chauhan Oct 17 '13 at 09:38
  • 1
    @GrijeshChauhan Ultimately, it's because he/she was referencing classes that weren't defined yet in the scope. Class definitions are statements, so they are evaluated in order and variable scoping rules apply. Here's an an example: https://gist.github.com/kevinastone/7022045 . In that example, if `x=1` was already defined in our scope, we'd get an error on the class definition of `A` (such as this example with `x` after the class definition): https://gist.github.com/kevinastone/7022068 – Kevin Stone Oct 17 '13 at 09:46
1

The classes Foo and Bar were not defined by the time name2class dictionary instantiation needed them. The other bug was no parameter being passed to Parent.do()

class Parent(object):
    def __init__(self):
        self.name2class = {'foo' : Foo, 'bar' : Bar}

    def do(self, string):
        return self.name2class[string]()

class Foo(Parent):
    pass

class Bar(Parent):
    pass

if __name__ == '__main__':
    parent = Parent()
    instance = parent.do('foo')
timcour
  • 384
  • 3
  • 9
  • I forgot to mention in my code, that the parent class gets arguments in __init__(self,telgram): self.telgram = telgram. Otherwise your solution would work fine ;) – uloco Oct 17 '13 at 10:16
1

You can use python type(name, bases, dict) functuion:...

Not Working


OK. Take a look here:

class Parent(object):

        childs = {}
        def somefunc(self):
                print "Hello from, %s"%self

        def do(self, string):

                return self.childs[string]()

class Foo(Parent):

        pass

class Bar(Parent):

        pass

parent = Parent()
parent.somefunc()
parent.childs["foo"] = Foo
parent.childs["bar"] = Bar
foo = parent.do("foo")
foo.somefunc()
bar = parent.do("bar")
bar.somefunc()

Which gives me:

Hello from, <__main__.Parent object at 0x...>
Hello from, <__main__.Foo object at 0x...>
Hello from, <__main__.Bar object at 0x...>
JadedTuna
  • 1,783
  • 2
  • 18
  • 32
  • The new classes won't have the methods defined in class Foo then. – uloco Oct 17 '13 at 09:16
  • @drrtyrokka, check out my modified answer – JadedTuna Oct 17 '13 at 09:23
  • Thanks but the factory solution is a better one for me, though this one works too. – uloco Oct 17 '13 at 09:27
  • @Vik2015: you obviously misunderstand `type`. First point, It's not ia function, it's a class. Second point: it's the default metaclass. IOW: it's main role is to create _new_ classes, which is what it does the way you call it. In fact the `class` statement is mostly syntactic sugar for a call to `type` with name, bases and dict. To make a long story short: your first example doesn't instanciate the `Foo` or `Bar' subclasses, it _creates_ new subclasses (with an empty namespace) and instanciate them. – bruno desthuilliers Oct 17 '13 at 11:24
  • @brunodesthuilliers, I understood this. Thats why I made second example – JadedTuna Oct 17 '13 at 11:25
  • @Vik2015 : it might be better to edit your post and remove the first example then - some people might not read the comments... – bruno desthuilliers Oct 17 '13 at 14:55
  • Also in your second example you update a class attribute on an instance, which can be quite confusing. – bruno desthuilliers Oct 17 '13 at 14:56
0

Defining a new child class should not impact the base class implementation. The appropriate design pattern here is the Factory. There are many ways to implement it, from a simple function to a dynamic registration mechanism. The simpler version:

class Parent(object):
   # your code here
   pass

class Child1(Parent):
   # XXX

class Child2(Parent):
   # XXX

class Child2(Parent):
   # XXX


CLSMAP = {
    "name1": Child1,
    "name2": Child2,    
    "name3": Child3,
    }   

def create(name, *args, **kw):
   return CLSMAP[name](*args, **kw)
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118