Klasse über klass variable dynamisch aus String 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
ready
User
Beiträge: 33
Registriert: Sonntag 15. Juni 2008, 12:21
Kontaktdaten:

Hallo Leute,

ich habe eine Frage. Und zwar habe ich folgenden Code:

Code: Alles auswählen

if d.name == "ms2": klass = ms2
elif d.name == "ms3": klass = ms3
elif d.name == "mmv": klass = mmv
elif d.name == "mov": klass = mov
elif d.name == "media": klass = media
elif d.name == "sonderposten": klass = sonderposten
            
row = klass.objects.filter(cquery.count_null(f.key)).count()
ms2 / ms3 /mmv / mov / media / sonderposten sind natürlich Klassen die hier der klass Variable zugewiesen werden.
Etwas weiter unten wird die klass Variable dann aufgerufen.

Jetzt möchte ich mir diesen ganzen if Wust gerne sparen, quasi schreiben:
klass = d.name
Nur das geht halt schlecht weil d.name ein String ist. Gibt es eine Möglichkeit den String d.name zu einer Klasse umzuwandeln, so dann ich klass dynamisch laden kann?
Danke!

MfG ready
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Ich würde ja ein Mapping via Dictionary vorschlagen.

Code: Alles auswählen

import operator
mapping = {
    '+': operator.add,
    '-': operator.sub
}

print mapping['+'](5, 3)
print mapping['-'](5, 3)

Mit eval ist so etwas möglich was du möchtest, aber ich halte den Mapping-Ansatz für besser.

Code: Alles auswählen

class Foo():
    def __init__(self):
        self.bar = 23

baz = eval('Foo')()
print baz.bar
ready
User
Beiträge: 33
Registriert: Sonntag 15. Juni 2008, 12:21
Kontaktdaten:

Hi /me,

also mir gefällt die eval Möglichkeit besser :)
Habe jetzt mal folgendes versucht:

Code: Alles auswählen

klass = eval(d.name)
row = klass.objects.filter(cquery.count_null(f.key)).count()
Funktioniert einwandfrei. Super, tausend Dank! :)

MfG ready
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Das Problem ist allerdings, dass die eval keine Lösung ist. Mit eval kannst du beliebigen Code ausführen, was zu nahezu beliebigen Fehlermedlungen führen kann. Hinzu kommt, was noch wesentlich schlimmer ist, dass eval eventuell über einen mehr oder weniger verschlungenen Weg Benutzereingaben erhält. Dann kann man für wirklich nichts mehr garantieren, da dein Programm dann tatsächlich beliebig manipulierbar ist. Im schlimmsten Fall ist deine Anwendung auch noch über das Web erreichber, dann kannst du dir beliebige Szenarien ausmalen.

Also als kleiner Merksatz: immer wenn du in die Versuchung kommst eval zu verwenden, dann machst du einen riesigen Designfehler.

/me hat mit den Dictionaries bereits eine vernünftige Lösung genannt, was fast immer die Lösung bei Problemen ist, wenn man etwas mit eval versucht.
Das Leben ist wie ein Tennisball.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

ready hat geschrieben:also mir gefällt die eval Möglichkeit besser :)
Stelle bitte sicher, dass du d.name absolut unter Kontrolle hast. eval führt beliebigen Python-Code aus und reißt häufig große Sicherheitslöcher in Programme.
deets

Fuer sowas kann man zB Metaklassen benutzen - ganz sicher besser als eval.

Code: Alles auswählen

class ClassNameMapped(type):
    def __new__(mcls, name, bases, d):
        clazz = type.__new__(mcls, name, bases, d)
        clazz.NAME2CLS[name] = clazz
        return clazz

    def class4name(cls, name):
        return cls.NAME2CLS[name]

class BaseA(object):

    __metaclass__ = ClassNameMapped
    NAME2CLS = {}

class Foo(BaseA):
    pass

class Bar(BaseA):
    pass

print BaseA.class4name("Foo")
ready
User
Beiträge: 33
Registriert: Sonntag 15. Juni 2008, 12:21
Kontaktdaten:

Ja ihr habt schon recht, eval ist mit Vorsicht zu geniessen.
In meinem Fall ist es aber so, dass d.name nur von mir über ein Import Script vergeben wird. Hängt also von keiner Benutzereingabe ab oder ähnlichem.
Der Grund warum ich mich gegen ein Mapping entschieden hab ist folgender:
Zu meinen Klassen: ms2 / ms3 /mmv / mov / media / sonderposten .. können später noch welche hinzukommen, in der alten Version und in der Mapping Version muss ich, wenn ich neue Klassen hinzufüge, viele Stellen im Code warten.
Okay ich könnte jetzt die Mappings in einer zentralen Datei festlegen (z.B. settings.py) und diese in mehrere Dateien importieren, eben dort, wo ich es brauche.
Aber dennoch gefällt mir die eval Lösung etwas besser, denn so muss ich nur mein Import Script warten, und wie gesagt, d.name kann nur ich bearbeiten und sonst keiner!
Danke

MfG ready
deets

Und irgendwann uebergibst du das Skript an wen anders, und der kommt dann auf die Idee das aus ner Konfigurationsdatei zu lesen oder ein Webfrontend zu bauen, und... bums. ich habe in 15 Jahren Python-Entwicklung noch nicht *einmal* ein Problem gehabt, dass ich mit eval haette loesen muessen. Und du musst auch nix zentrales machen, sondern kannst - wie ich gezeigt habe zB implizit durch eine Metaklasse, oder explizit durch eine Basisklasse - den Aufwand auf eine Zeile Code pro Klasse an der Stelle, wo sie definiert wird, beschraenken. Wenn dir das schon zu viel ist, dann hast du ein Problem.
ready
User
Beiträge: 33
Registriert: Sonntag 15. Juni 2008, 12:21
Kontaktdaten:

Hallo deets,

ich würde deine Lösung gerne in Betracht ziehen, aber ich verstehe sie noch nicht wirklich.
Kannst du mir vielleicht zeigen wie deine Lösung mit meinem Codebeispiel aussehen würde?
Hier nochmal das Beispiel mit eval:

Code: Alles auswählen

klass = eval(d.name)
row = klass.objects.filter(cquery.count_null(f.key)).count()
Vielen Dank

MfG ready
deets

Wieviel mehr Code-Beispiel brauchst du denn, als den *Code*, den ich gepostet habe? Der macht doch genau das?!? Statt "eval(d.name)" eben BaseA.class4name(d.name)....
ready
User
Beiträge: 33
Registriert: Sonntag 15. Juni 2008, 12:21
Kontaktdaten:

Ja also ich denke ich habs kapiert. Müssten meine ms2 / ms3 /mmv / mov / media / sonderposten also noch zusätlich von BaseA erben wenn ich es richtig verstanden habe?!

MfG ready
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

ready hat geschrieben:Ja also ich denke ich habs kapiert. Müssten meine ms2 / ms3 /mmv / mov / media / sonderposten also noch zusätlich von BaseA erben wenn ich es richtig verstanden habe?!
Ja, damit sie die Metaklasse bekommen.

Bei der PYCON in Leipzig gibt es übrigens einen Vortrag von Andi Albrecht über "Metaprogrammierung, praktisch". :-)
Antworten