Dynamischer Dispatch

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
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

Ich bin immer noch mit den Meta-Programmierungs Grundlagen beschäftigt (wobei ich versuche Ruby-Beispiele in Python zu übertragen). Mich interessieren Verbesserungsvorschläge/Alternativen zu folgendem Snippet:

Code: Alles auswählen

>>> tags = ['a', 'html', 'div', 'head', 'body', 'h1', 'h2', 'h3', 'span']
>>> class HtmlMe (object):
		pass

>>> html = HtmlMe
>>> def tag_me(name, val):
		return "<" + name + ">" + val + "</" + name + ">"

>>> for tag in tags:
		setattr(html, tag, partial(tag_me, tag))

>>> html.html(html.body(html.h1("Hello World")))
'<html><body><h1>Hello World</h1></body></html>'
EDIT: Einrückung angepasst
Achtung: User ist ein Python-Lehrling!
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Machst du das zur Übung oder willst du einfach nur mit durch Funktionen dargestellten Elementen arbeiten? Falls letzteres der Fall ist, willst du vielleicht mal einen Blick in die Elemente für die E-Factory werfen, die lxml so mitliefert.
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

snafu hat geschrieben:Machst du das zur Übung (...)
Ja, es geht um 's lernen!
Achtung: User ist ein Python-Lehrling!
BlackJack

@Ronnie: Also ich finde das Beispiel schlecht. Wozu die Klasse? Die macht als Klasse überhaupt keinen Sinn sondern dient einfach nur als Namensraum.

Das würde ich in Python eher als Klasse mit Metaklasse lösen, oder mit dynamischen Attributzugriffen via `__getattr__()`. Dann könnte man zum Beispiel auch Unterklassen erstellen die mehr können, zum Beispiel verschiedene Versionen von HTML oder Mixins für SVG oder MathML.
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

@BlackJack: erstmal vielen Dank für die Rückmeldung. Könntest du ein einfaches Beispiel liefern, oder alternativ eine Empfehlung wo ich mehr dazu lesen kann?
Achtung: User ist ein Python-Lehrling!
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Die Lösung, die ich zuerst in Python bauen würde, ist diese:

Code: Alles auswählen

class Element:
    def __init__(self, name):
        self.name = name
    
    def __call__(self, *args):
        return "<%s>%s</%s>" % (self.name, "".join(a for a in args), self.name)

class Html:
    def __getattr__(self, name):
        return Element(name)

h = Html()

print(h.html(h.body(h.h1("Hello"))))
Sie ist objektorientiert und fummelt nicht an den dicts einer Klasse herum, was ich als Monkey-Patching bezeichnen würde und nur im Notfall einsetzen würde. Sie ist aber recht lang.

Mit `partial` geht es in der `setattr`-Variante auch kürzer:

Code: Alles auswählen

def element(name, *args):
    return "<%s>%s</%s>" % (name, "".join(a for a in args), name)

class h: pass

for name in "html body h1".split():
    setattr(h, name, partial(element, name))

print(h.html(h.body(h.h1("Hello"))))
Ich nutze hier die Klasse als ein Modul für Arme. Ich mache mir nicht die Mühe, ein Exemplar davon zu bilden, sondern rufe statische Funktionen auf. Finde ich aber immer noch besser, als in einem Modul alles globale Funktionen anzulegen.

Doch ich sagte ja, `setattr` fühlt sich nicht gut an. Da baue ich mir die Klasse lieber gleich selbst:

Code: Alles auswählen

h = type("h", (), { n: partial(element, n) for n in "html body h1".split() })

print(h.html(h.body(h.h1("Hello"))))
Das letzte Beispiel erfordert Python 3.1. In Python 2.x ist eine dict-comprehension nur über Umwege möglich. Für solche Experimente ist aber ein aktuelles Python IMHO genau das richtige.

Stefan
BlackJack

@sma: "Nur über Umwege möglich" klingt irgendwie als wenn es komplizierter oder länger wäre. Ich würde das hier nicht als "Umweg" bezeichnen, sondern als ziemlich direkten Weg das in 2.x zu lösen:

Code: Alles auswählen

h = type("h", (), dict((n, partial(element, n)) for n in "html body h1".split()))
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

@sma, BlackJack: Vielen herzlichen Dank! Ich freue mich immer, wenn ich was dazu lernen kann! Jetzt muss es nur noch langsam ins Langzeitgedächtnis sickern.
Achtung: User ist ein Python-Lehrling!
Antworten