Seite 1 von 2
Wie eigene Typen definieren?
Verfasst: Donnerstag 18. September 2008, 11:09
von snafu
Hi!
Ich plane mit einer Funktion namens
prettify() die Objekte verschiedener anderer Funktionen, welche von diesen immer als Tuple-Liste zurückgegeben werden, lesbarer darzustellen. Hierfür müssten für jedes Listenelement die Elemente eines jeden Tuples mittels String Formattings in eine bestimmte Form gebracht und dann alle Elemente zu einem String zusammengefügt werden, welcher dann zurückgegeben wird. Bisher mache ich das so:
Code: Alles auswählen
def prettify_moods(data):
s = []
for elem in data:
name, string, time, mood = elem
s.append('<%s> %s <%s>\n%s' % (name, string, time, mood))
return '\n\n'.join(s)
Ich möchte nun, dass in diesem Fall das Objekt
data von der Funktion, die das Objekt zurückgibt (in meinem Fall heißt sie
get_moods()) den Typ
moods erhält, damit die spätere Funktion
prettify() prüfen kann, um welchen Typ es sich handelt und dann entscheidet, wie das entsprechende String Formatting aussehen muss.
Das war quasi die Erklärung für die Frage aus der Überschrift - ich hoffe, es ist nachvollziehbar, was ich vorhabe.
Gruß
Sebastian
Verfasst: Donnerstag 18. September 2008, 11:16
von sma
Also ich verstehe nicht, was du willst. Suchst du danach?
Code: Alles auswählen
class Mood(object):
...
def get_moods():
return [Mood(), Mood(), ...]
for obj in get_moods():
if isinstance(obj, Mood):
...
Stefan
Verfasst: Donnerstag 18. September 2008, 11:20
von BlackJack
Deine Frage im Titel geht schon in die richtige Richtung: Deine Objekte sollten selber wissen wie sie "prettified" werden, also musst Du Typen erstellen, die eine entsprechende Methode haben. Eigene Typen erstellt man mit ``class``. Das allgemeine `prettify()` sähe dann ungefähr so aus:
Code: Alles auswählen
def prettify(data):
return '\n\n'.join(d.prettify() for d in data)
Der Ansatz, den sma da zeigt, ist eher unschön.
Verfasst: Donnerstag 18. September 2008, 11:33
von audax
Verfasst: Donnerstag 18. September 2008, 11:35
von snafu
BlackJack hat geschrieben:Eigene Typen erstellt man mit ``class``.
Sorry, das habe ich nicht ganze verstanden. Wenn ich die Klasse
Test erstelle, hat sie doch immer noch den Typ
instance:
Code: Alles auswählen
>>> class Test:
pass
>>> test = Test()
>>> type(test)
<type 'instance'>
Davon habe ich ja nicht wirklich was. Aber vermutlich meintest du das auch anders...
Verfasst: Donnerstag 18. September 2008, 11:43
von Leonidas
Eigene Typen definierst du mit ``class``. Eine Klasse ist ein Typ, so einfach ist das. Und jeder Typ weiß, wie man sich darstellt, über ``render``. Oder irgendeine andere Funktion.
Code: Alles auswählen
class RenderingNode(object):
def render(self):
return self.content
class Text(RenderingNode):
def __init__(self, text):
self.content = text
class Number(RenderingNode):
def __init__(self, number):
self.content = number
nodes = [Text("Ich bin"), Number(3), Text("Jahre alt")]
print ' '.join(node.render() for node in nodes)
Verfasst: Donnerstag 18. September 2008, 11:51
von jens
Verfasst: Donnerstag 18. September 2008, 13:21
von snafu
Vielleicht verstehe ich euch alle nur falsch, aber ich möchte eigentlich sowas machen können:
Code: Alles auswählen
def prettify(data):
datatype = type(data)
if datatype is moods:
return _prettymoods(data)
if datatype is bulletins:
return _prettybulletins(data)
else:
raise TypeError, 'Expect moods or bulletins type, not %s' % datatype
def _prettymoods(data):
hier der selbe inhalt wie oben beschrieben
Und mir ist bisher nicht klargeworden wie ich das umsetzen soll.
Verfasst: Donnerstag 18. September 2008, 13:39
von cofi
Du suchst wohl nach isinstance()?
Und natürlich hat eine Instanz einer Klasse den Typ Instanz

Klassenvergleiche kannst du dann mit isinstance machen.
Verfasst: Donnerstag 18. September 2008, 13:42
von jens
snafu hat geschrieben:Vielleicht verstehe ich euch alle nur falsch, aber ich möchte eigentlich sowas machen können:
Leonidas variante mit render() ist aber IMHO besser.
Verfasst: Donnerstag 18. September 2008, 14:16
von Leonidas
snafu hat geschrieben:Vielleicht verstehe ich euch alle nur falsch, aber ich möchte eigentlich sowas machen können
Das ist aber keine gute idee, da die Render-Logik dann in eine ungebeiligte Funktion verschoben wird, die zudem nicht erweiterbar ist. Meine Variante ermöglicht belibige Erweitarbarkeit, da um es zu rendern nur ein Interface implementiert werden muss, schon funktioniert das Rendern wie mit bereits fertigen Klassen.
Verfasst: Donnerstag 18. September 2008, 15:08
von Hyperion
Das Prinzip, das Leonidas vorgeschlagen hat, ist doch dabei eine wirklich gängige und sinnvolle Lösung. Spontan fällt mir bei Java die toString()-Methode ein, die afaik
jedes Objekt besitzt.
Ok, bei Python ja auch fällt mir grad auf
Will man eine eigene Form der Ausgabe überschreibt man eben __str__() oder __repr__() und voila. Nichts anderes wird doch von Dir verlangt, oder?
Verfasst: Donnerstag 18. September 2008, 16:34
von snafu
Habe Leonidas' Tipp nun erfolgreich angewendet:
http://paste.pocoo.org/show/85606/
Vielen Danke dafür.
Anmerkung: Die fehlenden Klammern in Zeile 53 sind mir später auch aufgefallen und bei mir nun gesetzt.

Verfasst: Donnerstag 18. September 2008, 16:51
von BlackJack
Nein, Du hast Leonidas' Tipp nicht erfolgreich angewendet. Warum kannst oder willst Du den Klassen, die Du *sowieso schon hast*, keine `prettify()`-Methode verpassen? So wie's jetzt ist, ist es ja noch bescheuerter gelöst als die hässliche Nicht-OOP-Lösung mit Typtests. `Toolbox` ist im Moment vollkommen überflüssig.
Verfasst: Donnerstag 18. September 2008, 20:27
von snafu
BlackJack hat geschrieben:Nein, Du hast Leonidas' Tipp nicht erfolgreich angewendet. Warum kannst oder willst Du den Klassen, die Du *sowieso schon hast*, keine `prettify()`-Methode verpassen? So wie's jetzt ist, ist es ja noch bescheuerter gelöst als die hässliche Nicht-OOP-Lösung mit Typtests. `Toolbox` ist im Moment vollkommen überflüssig.
Hab's in der neuen Version beachtet:
http://paste.pocoo.org/show/85627/
Verfasst: Donnerstag 18. September 2008, 20:39
von Hyperion
Wo denn? Du hast doch schon wieder eine Fallunterscheidung drin (wobei ich das return nach dem if unübersichtlich finde! Ein explizites else ist da irgend wie schöner

)
Verfasst: Freitag 19. September 2008, 07:43
von jens
Verfasst: Freitag 19. September 2008, 10:10
von snafu
Irgendwie check ich das mit den Klassen noch nicht richtig. Ich habe jetzt das hier gebastelt:
Code: Alles auswählen
class Generic(object):
def get_dict(self):
"""Return a dictionary of given data, expects a list with tuples
For each tuple: Return first element as key and a tuple of remaining
elements as value.
Example:
In: [('foo', 'bar', 'baz'), ('ham', 'egg', 'spam')]
Out: {'foo': ('bar', 'baz'), 'ham': ('egg', 'spam')}
Intended to ease access on user related data when setting tuples True.
"""
keys = (elem[0] for elem in self.data)
values = (elem[1:] for elem in self.data)
return dict((key, value) for key, value in izip(keys, values))
class Moods(Generic):
def __init__(self, data):
self.data = data
def prettify(self):
"""Return a human-readable string of all data"""
data = self.data
if type(data) is tuple:
string, time, mood = data
return '%s <%s>\n%s' % (string, time, mood)
s = []
for elem in data:
name, string, time, mood = elem
s.append('<%s> %s <%s>\n%s' % (name, string, time, mood))
return '\n\n'.join(s)
Meine get_moods aus Datagetter erzeugt nun also ein Moods Objekt. Diese Klasse erbt von Generic die Methode get_dict (zumindest glaube ich das). Wenn ich jetzt sage:
Code: Alles auswählen
datagetter = pyspace.Datagetter()
[dann der login...]
moods = datagetter.get_moods()
moods.get_dict()
...dann kommt ein leeres Dict zurück. Verschiebe ich get_dict in die Klasse Moods, funktioniert es wunderbar. Wo liegt der Fehler?
Verfasst: Freitag 19. September 2008, 10:13
von bwbg
Code: Alles auswählen
# ...
class Printer(object):
def printout(self):
print '...Printer prints something...'
class OtherPrinter(object):
def printout(self):
print '...OtherPrinter prints something else...'
class MessagePrinter(object):
def __init__(self, message):
self.message = message
def printout(self):
print '...MessagePrinter prints "%s"' % (message,)
# ...
object_list = [Printer(), OtherPrinter(), MessagePrinter('Hallo Welt'), Printer()]
for o in object_list:
o.printout()
Wenn ich das Problem richtig interpretiere, dann sollte oben genanntes Modell genau auf das Problem des OP passen. In C++ würde man eine (abstrakte) Basisklasse mit einer rein virtuellen Methode printout definieren und die Printer davon erben lassen - in Java wären das Interfaces. Soweit ich jetzt richtig informiert bin, ist das in Python nicht nötig.
Grüße... Heiko
EDIT: Einrückung korrigiert (böse Tabs)
Verfasst: Freitag 19. September 2008, 10:16
von jens
Code: Alles auswählen
import pprint
def prettify(data):
return pprint.pformat(data)
def get_dict(data):
"""
>>> get_dict([('foo', 'bar', 'baz'), ('ham', 'egg', 'spam')])
{'foo': ('bar', 'baz'), 'ham': ('egg', 'spam')}
"""
result = {}
for item in data:
result[item[0]] = item[1:]
return result
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=False)
d = get_dict([('foo', 'bar', 'baz'), ('ham', 'egg', 'spam')])
print prettify(d) # oder gleich pprint.pprint(d) ;)