tags:

views:

101

answers:

6

Hi,

I'm sorry if I wasn't clear enough in the title, don't hesitate to correct it if you find a better way to express that:

I have a file where there are class names, i.e.:

classa
classb
classb
classc
classc
classc

Then I want to read it line by line and to dynamically create that class. I would do something like that in php:

while (!eof())
{
   $class=fread(..)
   $tab[] = new $class();
}

How would you do that in python (if it's possible)?

Thanks a lot!

Edit: after reading the answers, to be more precise on what I'm planning to do: I'm not planning to do such a simple stuff. It will be far more complex: I want a user who doesn't know programming to edit a simple text file, and to copy/paste some declarations and change their properties and to re-launch a kind of parser which will re-run a batch and show the result of complex operations. Simplified Example of a file:

car:(red,4_wheels,4_places)
bike:(blue,2_wheels,1_place)

Then the user will change it to:

car:(red,4_wheels,4_places)
car:(yellow,4_wheels,2_places)
bike:(blue,2_wheels,1_place)
bike:(green,2_wheels,2_places)

And then with python I'll read this file, create two instances of the class car, and two instances of the class bike. Thus a user who doesn't understand / know python will be able to edit the file without touching a line of code. I think this is the right way to go, if you have any other suggestions for this code, you're welcome!

Olivier Pons

+1  A: 

Reading each line from the file is pretty easy:

with open(filename) as f:
    for line in f:

The first thing that comes to mind for class creation is the type function:

        cls = type(line, (object,), {})

This will create a new empty class which is a subclass of object and has a name given by the contents of the line.

I have to wonder why you're trying to do this, though. An empty class like that doesn't seem very useful in Python.

David Zaslavsky
I guess, "dynamically create that class" should really be read as "dynamically create *an instance* of that class". That's what the PHP snippet in the OP does.
lunaryorn
Ah, in that case that is really not a good idea.
ternaryOperator
ohhh, I see I shouldn't have skimmed over that PHP code sample.
David Zaslavsky
Err well i'm sorry but I have to vote it down because it doesn't answer properly to my question, but this is a nice piece of code, I'll keep it for futur use if I need it one day! Thanks again
Olivier Pons
@Olivier: do bear in mind that it's a pretty "hackish" thing to do and I'd be a little surprised if you ever find a good use for it. In retrospect I'm tempted to delete this...
David Zaslavsky
I'll use your code actually. To be more precise, my YAML file will have "class" declarations, i.e. "car" then later on, will have class instanciation i.e. car:id=145,color='blue' and so on. I'll have to kind of "create" dynamically new classes then instanciate them later on. Thanks again for this hint! Tell me if I'm wrong but your exemple says "Hey Python please create a new class named 'line'". Am I right? If so, later on, I will instanciate this class via this: "myobject= new cls()". Am I still right?
Olivier Pons
Actually my example tells Python to create a new class with a name given by the value of the variable `line` (the class isn't actually named "`line`"). Then you can instantiate it with `myobject = cls()`. (Python doesn't have a `new` keyword)
David Zaslavsky
+3  A: 

Assuming your classes are already in scope (i.e. they're not in a separate module), you can refer to a class by its name very easily through the globals() dict. For example:

class Foo:
    pass

foo_cls = globals()['Foo']
foo = foo_cls()
# foo is now an instance of __main__.Foo
Vineet
That's a very nice answer, I was just wondering if accessing globals is not a kind of a trick, because I've always been taught to avoid globals...
Olivier Pons
A: 

This should be fairly easily possible using a dictionary of classes (not that this is not necessarily a restriction as each python namespace can be accessed as a dictionary so if you want these classes say to all be within a module or another class, just replace classes with the __dict__ attribute of the class or use globals as others have suggested):

classes = dict()
with open('filename') as f:
    for line in f:
        classes[line] = class()

(Implementation details may vary).

You may however want to look into using pickling instead as on the face of it, this approach seems flawed (it might work well in PHP though :-) ).

ternaryOperator
+1  A: 

Assuming that all classes are declared in a module foo:

classname = sys.stdin.read().rstrip()
cls = getattr(foo, classname)()

To access classes in the same module, use the builtin globals() function.

lunaryorn
+2  A: 

The other answers do what you asked, but I wanted to add a small measure of flexibility (and protection.) I would use a dictionary to map the line names to the class objects, so you're not letting the text file instantiate anything it wants. It has to be a class that you allow it to, in your code. This also makes it easier because you can change names on either side without trouble (and you could map multiple line-names to a single class name, if you wanted.)

classes = {'classa': classa, 'classb': classb}
cls = type(classes[line], (object,), {})  ## or whichever method to instantiate you prefer

But in general, it's not a very Pythonic thing to do, in my opinion.

zekel
Is this classes = {'classa': classa, 'classb': 'classb'}or classes = {'classa': classa, 'classb': classb}or classes = {'classa': 'classa', 'classb': 'classb'} ?
Olivier Pons
Whoops, it should be {'classfilename': classname}. I wrote that walking out the door. (Answer above fixed to reflect that.)
zekel
+1  A: 

As you are using YAML anyway, consider using PyYAML with the serialized classes deriving from the yaml.YAMLObject metaclass or registering your own represenenter.

From the documentation of PyYAML:

class Monster(yaml.YAMLObject):
    yaml_tag = u'!Monster'
    def __init__(self, name, hp, ac, attacks):
        self.name = name
        self.hp = hp
        self.ac = ac
        self.attacks = attacks
    def __repr__(self):
        return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
            self.__class__.__name__, self.name, self.hp, self.ac, self.attacks)

print yaml.load("""
--- !Monster
name: Cave spider
hp: [2,6]    # 2d6
ac: 16
attacks: [BITE, HURT]
""")

prints Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])

That way you can leave out many of the code you need for error handling (e.g. class is not present) and you also have system that is more robust against malicious configuration files. As an additional bonus, you are able to dump objects from your program into a YAML file.

nd
Depending on what he's doing, the YAML syntax might be more overhead than it's worth. Checking if the class exists seems like the only check he otherwise needs, given his question. But I like YAAML too.
zekel
There is a comment by the original poster on the question: "It's far more complex: I'll read a complex YAML file ..." - so I came to the conclusion "As you are using YAML anyway..."
nd
Well, at first sight I thought "Hey that's exaclty what I need" but it's 100% not the case. It's more complex than that. I think you put me on the right track, thank you very much indeed!
Olivier Pons