Seite 1 von 1
Klasse dynamisch (= anhand eines Strings) laden
Verfasst: Sonntag 2. August 2009, 21:49
von Jena
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
Verfasst: Sonntag 2. August 2009, 21:59
von cofi
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

Verfasst: Sonntag 2. August 2009, 22:11
von Jena
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.
Verfasst: Sonntag 2. August 2009, 22:19
von cofi
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.
Verfasst: Sonntag 2. August 2009, 22:26
von Jena
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
Verfasst: Sonntag 2. August 2009, 22:29
von cofi
Dir ist klar, dass du hier Klassen missbrauchst? Du Erstellst ein Exemplar, dass du fuer eine Funktion aufrufst und danach das Exemplar wieder wegschmeist.
Verfasst: Sonntag 2. August 2009, 22:33
von Jena
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.
Verfasst: Sonntag 2. August 2009, 22:50
von Jena
Danke, die Dict-Idee hat funktioniert:
Code: Alles auswählen
classes = {
'space' : SpaceTokenizer,
...
}
def factory(x, text):
return classes[x]().tokenize(text)
Grüße
Verfasst: Sonntag 2. August 2009, 22:53
von EyDu
Ein explizit angelegtes Dictionary ist aber die saubere Lösung.
Verfasst: Sonntag 2. August 2009, 22:58
von Leonidas
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)
Verfasst: Montag 3. August 2009, 09:24
von sma
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
Verfasst: Montag 3. August 2009, 16:29
von Jena
Vielen Dank für die vielen Anregungen!