Klasse dynamisch (= anhand eines Strings) laden

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Jena
User
Beiträge: 9
Registriert: Sonntag 2. August 2009, 21:35

Hallo @ all,


ich suche nach einem Weg, in Python eine Klasse zu laden, deren Name nur als String vorliegt. Es geht dabei um soetwas wie eine Factory-Funktion, die anhand eines Strings die entsprechende Klasse inszanziiert. Schematisch:

Code: Alles auswählen

class FooTest(object):
    def do_something(self):
        return 'foo'

class BarTest(object):
    def do_something(self):
        return 'bar'

def factory(string):
    class_name = string.capitalize() + 'Test'
    return ???(class_name).do_something()

>>> factory('bar')
-> 'bar'
Ich habe dazu bereits Google und die hiesige Forensuche ausgequetscht und das seit 2.6 obsolete new-Modul, __new__, __import__, usw. gefunden. Python spielt allerdings nicht mit und quittiert mir den Dienst, da die Klasse eben als str vorliegt.

Vielen Dank für eure Tipps im Voraus,


Jena
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Stichwort ``eval``

Du solltest aber vielleicht mal erzaehlen, was du vorhast, denn man sollte sich genau ueberlegen, wenn man so etwas macht und es sollte auch gut begruendet sein ;)
Jena
User
Beiträge: 9
Registriert: Sonntag 2. August 2009, 21:35

Ja, eval und Risiken und Nebenwirkungen sind mir durchaus bekannt aus anderen Programmiersprachen. :) Python ist allerdings noch Neuland für mich, an eval habe ich noch nicht gedacht.
Worum es konkret geht: Es gibt mehrere Klassen, die einen String anhand eines bestimmten Kriteriums segmentieren. So ein Kriterium könnte z.B. Leerzeichen sein, dafür gibt es dann eine spezifische Klasse. Die Factory-Funktion erhält das Kriterium (natürlich als str), bastelt daraus den entsprechenden Klassennamen (ebenfalls str) und soll die entsprechende Klasse instanziieren.

Im Grunde genommen (zumindest meines Erachtens nach) keine allzu exotische Sache.

Danke für die bisherige Hilfe btw. :)

P.S. Das Ganze ist natürlich auch nicht so generisch möglich, in dem ich explizit das Kriterium abfrage und die entsprechende Klasse lade. Ich würde das aber gerne - aus Überzeugung - vermeiden.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ich durchblicke das zwar nicht so recht, aber was spricht gegen ein Dictionary, das Kriterien auf Klassen mapt? Die Kriterien sind ja scheinbar definiert, genauso wie die Klassen.
Jena
User
Beiträge: 9
Registriert: Sonntag 2. August 2009, 21:35

Hm, ich werds mal probieren, danke!

Ich konkretisiere das Ganze aber hilfsweise nochmal. Möglicherweise bin ich diesbezüglich etwas "versaut" durch schwachtypisierte Sprachen wie PHP, in denen soetwas nicht unüblich ist.

Code: Alles auswählen

class SpaceTokenizer(object):
    def tokenize(self, text):
        return text.split(' ')

class FullstopTokenizer(object):
    def tokenize(self, text):
        return text.split('.')

def tokenize(x, text):
    class_name = x.capitalize() + 'Tokenizer'
    return class_name().tokenize(text)
class_name() ist hier nur repräsentativ gemeint; dass soetwas in Python nicht möglich ist, ist klar.


Grüße
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Dir ist klar, dass du hier Klassen missbrauchst? Du Erstellst ein Exemplar, dass du fuer eine Funktion aufrufst und danach das Exemplar wieder wegschmeist.
Jena
User
Beiträge: 9
Registriert: Sonntag 2. August 2009, 21:35

Nein, ich habe die Klassen hier auf ihre wesentliche Funktion gestutzt, um nur relevanten Code und damit das zugrundeliegende Prinzip zu zeigen. Sorry, falls ich das nicht deutlich genug gemacht habe.
Jena
User
Beiträge: 9
Registriert: Sonntag 2. August 2009, 21:35

Danke, die Dict-Idee hat funktioniert:

Code: Alles auswählen

classes = {
    'space' : SpaceTokenizer,
    ...
}

def factory(x, text):
    return classes[x]().tokenize(text)

Grüße
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Code: Alles auswählen

globals()[classname]
Ein explizit angelegtes Dictionary ist aber die saubere Lösung.
Das Leben ist wie ein Tennisball.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Außer dass es irgendwie etwas Java-Feeling hat ("Kingdom of Nouns"), hier mit Dicts und ohne ``eval``:

Code: Alles auswählen

class SpaceTokenizer(object):
    def tokenize(self, text):
        return text.split(' ')

class FullstopTokenizer(object):
    def tokenize(self, text):
        return text.split('.')

def tokenize(x, text):
    instance = {'Space' : SpaceTokenizer,
        'Fullstop' : FullstopTokenizer
    }[x.capitalize()]()
    return instance.tokenize(text)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Wie wär's damit?

Code: Alles auswählen

class Tokenizer:
    def tokenize_by_space(self, s):
        return s.split()
    
    def tokenize_by_dot(self, s):
        return s.split(".")
    
    @classmethod
    def by(self, name):
        return getattr(Tokenizer(), "tokenize_by_" + name)

print Tokenizer.by("space")("a b.c")
Hält `Tokenizer` keinen weiteren gemeinsamen Zustand, kann man auch einfach modulglobale Funktionen benutzen. Ich bin mit der Benennung nicht 100% zufrieden, aber die Idee sollte klar sein.

Stefan
Jena
User
Beiträge: 9
Registriert: Sonntag 2. August 2009, 21:35

Vielen Dank für die vielen Anregungen!
Antworten